PDA

View Full Version : [C] Pazzesco! Istruzione successiva manda in errore la precedente!!MA COME?!


parisisalvo
18-11-2005, 17:25
Salve!!
Io programmo da una decina d'anni, e una cosa del genere non mi era mai successa.
Ecco lo scenario :


double div; //globale

main {

div=xxxx.yy; (un valore qualsisasi)
... creo socket datagram
... creo thread che gestisce il socket
}

void* thread(void* s)
{
unsigned long long cl;
s è il descrittore del socket
n=recv(s, ... ... ... );
rdtscll(cl);
cl/=div;
}


Allora, su UNA delle mie 3 macchine cosa accade :
che se nel codice lascio cl/=div; la recv va sempre in errore ancora prima che venga eseguita la divisione!!!! Mi torna in n il valore -1 !!!
Ossia :
n=recv(s, ... ... ... );
printf("%d\n",n); ---> a schermo stampera' -1
rdtscll(cl);
cl/=div;

Se invece al posto di cl/=div, metto cl=cl/(unsigned long long)div; tutto funziona!! Ossia in n ho il numero di byte giusti!
Il bello è che, in un'altra versione dello stesso codice, la recv funziona con la divisione sottostante...

La domanda è : Come CAVOLO fa una istruzione non ancora eseguita a far andare in errore quella precedente????????

cionci
18-11-2005, 17:48
A me mi puzza di buffer overflow...

jappilas
18-11-2005, 18:15
che macchina (nel senso, che cpu) è quella su cui ti si presenta l' errore?

mi vien l' impressione che sia un problema di pipeline e modo in cui la specifica macchina gestisce le eccezioni al proprio interno (che giusto per complicare un po' le cose , varia da un processore all' altro)...

parisisalvo
18-11-2005, 18:22
Nel portatile con Centrino M740 non va, invece nel Centrino M725 tutto ok, posso eseguirlo sia in un modo e a maggior ragione nell'altro.
Sia su un XP 2600+ che su vecchiotto Athlon 1Ghz, funziona come nell'M725...

Black imp
19-11-2005, 03:08
Salve!!
Io programmo da una decina d'anni, e una cosa del genere non mi era mai successa.
Ecco lo scenario :


double div; //globale

main {

div=xxxx.yy; (un valore qualsisasi)
... creo socket datagram
... creo thread che gestisce il socket
}

void* thread(void* s)
{
unsigned long long cl;
s è il descrittore del socket
n=recv(s, ... ... ... );
rdtscll(cl);
cl/=div;
}


Allora, su UNA delle mie 3 macchine cosa accade :
che se nel codice lascio cl/=div; la recv va sempre in errore ancora prima che venga eseguita la divisione!!!! Mi torna in n il valore -1 !!!
Ossia :
n=recv(s, ... ... ... );
printf("%d\n",n); ---> a schermo stampera' -1
rdtscll(cl);
cl/=div;

Se invece al posto di cl/=div, metto cl=cl/(unsigned long long)div; tutto funziona!! Ossia in n ho il numero di byte giusti!
Il bello è che, in un'altra versione dello stesso codice, la recv funziona con la divisione sottostante...

La domanda è : Come CAVOLO fa una istruzione non ancora eseguita a far andare in errore quella precedente????????



scusa ma tu gestisci una variabile condivisa tra due thread - il main e quello legato al socket - senza un semaforo?
per me il problema è quello

cionci
19-11-2005, 10:21
scusa ma tu gestisci una variabile condivisa tra due thread - il main e quello legato al socket - senza un semaforo?
Se ci accede anche dopo la creazione del thread potrebbe essere anche quello...

parisisalvo
19-11-2005, 14:14
l'unica variabile condivisa è il descrittore del socket s...
questo numero non viene mai toccato...
io cmq, ne faccio una copia dentro il thread
con : int *socket=(int*)s;
cmq, nel main quel descrittore non viene usato, resta tale, finche non si chiude il main. Dopo aver creato il socket, il descrittore del socket nel main, nn viene toccato...il main poi resta vivo con un semplice while(1);
Tant'è vero che CODICE identicissimo, tranne che per la divisione, funziona PERFETTAMENTE.
Forse ho un po' troppo semplificato il codice...piu' realisticamente è cosi' :

double div; //globale

main {
int sk; //locale al main
div=xxxx.yy; (un valore qualsisasi)
... creo socket datagram -> sk;
... creo thread che gestisce il socket gli passo (void*)&sk
while(1);
}

void* thread(void* s)
{
int *sock=(int*)s;
int socket=*sock;
int n;
unsigned long long cl;
socket è il descrittore del socket
n=recv(socket, ... ... ... );
printf("Socket ha ricevuto %d byte\n",n);
rdtscll(cl);
cl/=div;
}

Per debug, ho messo un printf subito dopo la recv, ossia
printf("Socket ha ricevuto %d byte\n",n);
La printf è messa PRIMA della divisione di cl.
Se lascio la divisione (ossia, tre righe di codice dopo), la recv, andra' in errore, e la printf stampera' -1, se faccio la divisione "per esteso" stampera' 20 (il numero di byte, che effettivamente l'altro calcolatore invia).
La cosa che non mi capacita è :
Come fa la recv ad andare in errore per una istruzione non ancora eseguita???
La divisione, viene poi effettuata correttamente, ma la recv non funziona!!
Vi giuro, se cambio solamente cl/=div con cl=cl/(unsigned long long)div; la recv funziona, e come byte stampa 20.

jappilas
19-11-2005, 14:18
quello che mi fa strano e a cui sto ancora cercando un senso logico, è che su due macchine su tre non ti dia errori e sulla terza sì... o_O

parisisalvo
19-11-2005, 14:29
Ancora peggio!!!
Con l'amico mio, per cercare di risolvere abbiamo passato due ore...
a testare e provare linea per linea...
chi l'avrebbe mai detto che era una divisione???
La cosa ancora piu' assurda, è che essendo un progetto molto grosso, ha tante "relase", ogni settimana apportiamo delle migliorie, parti nuove ect ect, fino all'altro ieri, funzionava tutto perfetto (anke sul M740). Ossia Recv con successiva divisione cl/=divclock.
Ma la cosa ancor piu' strana è che solo una macchina presenta il problema!!!
Ossia non è un errore "oggettivo", ma soggettivo...
immaginate la mia crisi di nervi, come faccio a correggere un errore che non c'e'???? è da diventar matti! Su 3 macchine funziona, e sull'altra no. Come correggo un errore che in teoria, solo la mia "vede".
Dopo due orette, a testare linee di codice, e provare...abbiamo visto che era la maledettissima divisione!!!

Black imp
19-11-2005, 14:47
quando compilo sotto linux con più thread o addirittura processi mi capita che le print non vengano eseguite subito ma a volte ritardate.

se poi compili con qualche ottimizzazione può essere che una operazione venga svolta in un momento diverso da quello che ti aspetti. per questo ti dicevo di usare dei semafori. la var condivisa era div.

71104
19-11-2005, 15:25
secondo me si tratta di una race condition: forse il codice generato per l'operatore /= differisce da quello generato per = e / separati; controlla l'assembly generato dal tuo compilatore (a proposito, quale usi?).

parisisalvo
20-11-2005, 19:58
uso il gcc...
pero', compiliamo sempre su una sola macchina (dove abbiamo installato tutte le librerie) poi passiamo i files alle altre 3 (tra cui l'M740).

71104
20-11-2005, 21:00
uso il gcc...
pero', compiliamo sempre su una sola macchina (dove abbiamo installato tutte le librerie) poi passiamo i files alle altre 3 (tra cui l'M740). ad ogni modo è impossibile che il gcc compili differentemente su macchine diverse; intendevo dire che per capire la causa dovresti analizzare il codice assembly generato dal compilatore nei due diversi casi (è l'unica cosa che fa la differenza tra le due versioni del programma); molto probabilmente concluderai che si tratta di una race condition.
inoltre, lascia perdere questo fatto della divisione: cerca la causa del problema in maniera "standard"; in maniera "standard" se la recv ti restituisce -1 e tu non capisci perché, devi semplicemente chiamare WSAGetLastError (sempre che usi Winsock 2 su Windows, altrimenti non lo so).

cionci
20-11-2005, 21:02
uso il gcc...
pero', compiliamo sempre su una sola macchina (dove abbiamo installato tutte le librerie) poi passiamo i files alle altre 3 (tra cui l'M740).
Hai provato a ricompilare sulla macchina che presenta il problema ?

71104
20-11-2005, 21:05
Hai provato a ricompilare sulla macchina che presenta il problema ? secondo me non cambia nulla... e se cambiasse qualcosa mi preoccuperei davvero O_O

parisisalvo
21-11-2005, 23:08
Ora è ancora piu' grave!!!
Abbiamo fatto un "upgrade" del software...
mettendo quello che quella maledetta recv riceve in un vettore...
chissà per quale maledetto motivo, torna lo stramaledetto -1
Ovviamente SOLO sulla macchina M740!
L'applicazione è fatta con le GTK, le altre macchine hanno su la SuSe 9.3 l'M740 ha la Fedora Core 4. L'M740 è un Acer1692 e non ho potuto installare suse, solo fedora è stata la piu' compatibile (wireless card e s.video).
Voi che dite? puo' essere colpa della distro??

(Se dal codice rimuovo tutta la parte gtk, l'applicativo funziona sempre, inserendo la parte gtk, funziona con le particolarità).

Qu@ker
21-11-2005, 23:23
Ma a quale errore si riferisce quel -1? Valore di errno?

71104
22-11-2005, 00:44
(Se dal codice rimuovo tutta la parte gtk, l'applicativo funziona sempre, inserendo la parte gtk, funziona con le particolarità). in altre parole se ci rimuovi una parte che non c'entra niente funziona: è sicuramente race condition. ribadisco il consiglio di Quaker: errno?

cionci
22-11-2005, 00:55
Hai provato a ricompilare sulla macchina che presenta il problema ?
Ribadisco...

parisisalvo
22-11-2005, 14:13
sulla macchina M740, non ci sono le lib grafiche...sulla Suse si sn installate in un secondo qua' richiede cose che gia' sono installate (tipo pango atk gdlib) arrivate alla gtk mi da errore, ho provato a installare quelle di fedora native, ma è come se non ci fossero, appena do make, mi da errore su tutta la grafica...quindi per compilare devo eliminare la parte gtk, e quindi funziona.

Dite di inserire l'istruzione

n=recv(... ... ...)
if (n<0) perror(" Errore : ");

parisisalvo
22-11-2005, 15:41
tanto per completezza, la chiamata non è recv ma recvfrom (dato che è datagram)
Allora perror mi torna Invalid argument errno numero 22

Ora vediamo :
analizziamo il thread :

//globali
struct mydata data[30];
int index_data=0;

void* ascolta_net(void *s)
{
int n;
socklen_t len;
char *buffer;
char b1[100];
unsigned long long cl;
buffer=malloc(100);
int *sock=(int*)s;
int sock=*socket;
struct sockaddr_in rem;

while(1)
{
n=recvfrom(sock, buffer, sizeof(struct revento), 0, (struct sockaddr*)&rem,&len);
leggiclock(cl);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);

pthread_mutex_lock(&mutex_data);
data[data_index].sock=*sock;
data[data_index].remote_address=rem;
bcopy(buffer,data[data_index].buffer,n);
data[data_index].n=n;
data[data_index].cl=cl/(unsigned long long)divclock;
if (data_index<30) data_index++;
pthread_mutex_unlock(&mutex_data);

pthread_mutex_lock(&elabora_mutex);
elabora_flag=1;
pthread_cond_signal(&elabora_cond);
pthread_mutex_unlock(&elabora_mutex);

pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
}
}


Alla fine del thread, c'e' una condition variable, che abilita all'elaborazione dei dati del vettore data[]...
qui ho una situazione produttore/consumatore, il consumatore consuma non appena questo thread lo risveglia.
Il problema, di questa race condition, dove sta? per cosa è?

cionci
22-11-2005, 16:04
Già questo è un buon punto... Ora stampa il valore del socket e di tutti i parametri della recvfrom prima della chiamata...

parisisalvo
22-11-2005, 16:16
Ciao, nella recvfrom :

recvfrom(sock, buffer, 50, 0, (struct sockaddr*)&rem,&len);

sock -> descrittore del socket che io le passo
buffer -> zona di memoria vuota dove mette i dati (lo riempie lei)
50 -> nbyte max da ricevere
0 -> flag a SO_EE_ORIGIN_NONE (nn so cosa sia, ma è a zero)
rem -> struttura indirizzi, dove la recv va a mettere l'indirizzo di chi è che ha fatto la send
len -> dimensione della struttura che la recv andra' a modificare...
ossia, i parametri che io le passo sono
il sock, 50 e 0, gli altri li modifica lei, li riempie lei...posso postarti il valore del sock, fuori nel main, dentro nel thread e dopo la recv.

cionci
22-11-2005, 16:18
Stampa i vaori di socket, del puntatore a buffer e del sockaddr (puntatore e valore)...

parisisalvo
22-11-2005, 17:37
Ciao, allora
il socket l'ho printato
nel main dove lo creo
prima di chiamare il thread
nel thread prima della recv
nel thread dopo la recv
nel main alla fine
ed è sempre 6, quindi il descrittore credo non sia...
sockaddr è una struttura ha tanti campi, ti interessa suppongo sin_addr (io questo ho plottato) sia prima che dopo la recv ha un valore casuale, invece sulla macchina dove il codice funziona, prima ha un valore casuale poi il corretto ip della macchina che ha fatto il send.
Per plottare l'indirizzo del puntatore a buffer? che uso? %d ?

cionci
22-11-2005, 17:49
Fai una cast (int)buffer e poi stampa con %d...

Comunque mi sembra che tu abbia individuato il problema... Il sin_addr non è coerente...

A perte il controllo sull'errore che è giusto fare...non può essere un problema di firewall ? Lo usi con l'interfaccia di loopback per fare i test ? Il programma è un server o un client ?

Qu@ker
22-11-2005, 17:49
Se la malloc() fallisce buffer contiene NULL. (BTW, dov'e' la free()?)
b1 a che serve?
Qui mi pare ci sia un po' di confusione:

int *sock=(int*)s;
int sock=*socket;
....
data[data_index].sock=*sock;

Un'altra cosa da verificare: la Fedora 4 utilizza di default NPTL per i threads (e non piu' LinuxThreads). Cosa utilizza la Suse 9.3?

parisisalvo
22-11-2005, 18:06
Ho fatto una prova, ho definito rem
come struct sockaddr e non come struct sockaddr_in e pare che funzioni!!!
ora, come ricavo l'ip del mittente da una struttura sockaddr?

parisisalvo
22-11-2005, 18:12
si, ho sbagliato a copiare quel pezzo di codice
data[data_index].sock=sock;

b1 era una variabile di test, per fare prove a copiare ect ect (scusate)

Il programma è client, pero' anke su quello funzionante ci siamo accorti di una cosa STRANA...
1) prima della recv l'ip dentro rem è sballato
2) dopo la recv, resta ancora sballato (ma la "suse" cmq prende gli 8 byte, la fedora NO)
3) al secondo giro, è dentro un while uno, prima della recv è sballato
4) dopo la recv è giusto, e rimane giusto...

come ho detto abbiamo cambiato da sockaddr_in a sockaddr e ora pare funzioni
ma come ricavo l'ip da una sockaddr (non ha il membro sin_addr) help

VICIUS
22-11-2005, 18:36
si, ho sbagliato a copiare quel pezzo di codice
data[data_index].sock=sock;

b1 era una variabile di test, per fare prove a copiare ect ect (scusate)

Il programma è client, pero' anke su quello funzionante ci siamo accorti di una cosa STRANA...
1) prima della recv l'ip dentro rem è sballato
2) dopo la recv, resta ancora sballato (ma la "suse" cmq prende gli 8 byte, la fedora NO)
3) al secondo giro, è dentro un while uno, prima della recv è sballato
4) dopo la recv è giusto, e rimane giusto...

come ho detto abbiamo cambiato da sockaddr_in a sockaddr e ora pare funzioni
Questo succede perchè la recv e la recvfrom sovrascrivono la struttura quando vengono chiamate.

ma come ricavo l'ip da una sockaddr (non ha il membro sin_addr) help
Puoi fare tranquillamente un cast a sockaddr_in.

ciao ;)

parisisalvo
22-11-2005, 18:49
Questo succede perchè la recv e la recvfrom sovrascrivono la struttura quando vengono chiamate.


Puoi fare tranquillamente un cast a sockaddr_in.

ciao ;)

Perdonami, ma xche alla prima recv, non sovrascirvono la struttura?
cioe' è giusto che al 2 giro, sovrascriva, ma xche non lo fa al primo??
al primo giro, xche non scrive l'ip della macchina che senda??
Come un casta?
del tipo
struct sockaddr_in ciao;
struct sockaddr miao;

ciao=(sockaddr_in)miao; ?? cosi' nn mi funziona

cionci
22-11-2005, 18:55
Fai il cast con il puntatore...

parisisalvo
22-11-2005, 19:00
dici
&ciao=(sockaddr_in)&miao
cosi'? non funziona

VICIUS
22-11-2005, 19:03
Perdonami, ma xche alla prima recv, non sovrascirvono la struttura?
cioe' è giusto che al 2 giro, sovrascriva, ma xche non lo fa al primo??
al primo giro, xche non scrive l'ip della macchina che senda??
Come un casta?
del tipo
struct sockaddr_in ciao;
struct sockaddr miao;

ciao=(sockaddr_in)miao; ?? cosi' nn mi funziona
In effetti dovrebbe farlo sempre quindi anche al primo.
Per quanto riguarda il cast non scordarti struct.
ciao = (struct sockaddr_in) miao;

ciao ;)

cionci
22-11-2005, 19:04
No, così:

struct sockaddr_in *sa_in = (struct sockaddr_in *)&sa;

sa_in->sin_addr

parisisalvo
22-11-2005, 19:07
niente, dopo 3 prove, alla fine tra asterischi & e -> ho risolto...
vediamo se rimane stabile...
cmq ancora non ho capito xche TUTTO IL MONDO riesce con sockaddr_in meno che io...mha?!?

Qu@ker
22-11-2005, 21:44
RTFM!

goku@big:~$ man recvfrom -P cat | sed -ne '8,28p'
SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>

ssize_t recv(int s, void *buf, size_t len, int flags);

ssize_t recvfrom(int s, void *buf, size_t len, int flags, struct sock-
addr *from, socklen_t *fromlen);

ssize_t recvmsg(int s, struct msghdr *msg, int flags);

DESCRIPTION
The recvfrom and recvmsg calls are used to receive messages from a
socket, and may be used to receive data on a socket whether or not it
is connection-oriented.

If from is not NULL, and the underlying protocol provides the source
address, this source address is filled in. The argument fromlen is a
value-result parameter, initialized to the size of the buffer associ-
ated with from, and modified on return to indicate the actual size of
the address stored there.
goku@big:~$

parisisalvo
22-11-2005, 22:21
ciao Qu@ker, non ho afferrato.
dov'e' che ho sbagliato? cos'e' che non ha funzionato? devo inizializzare qualcosa?

Qu@ker
22-11-2005, 22:46
Qualcosa tipo:

len = sizeof rem;

prima di chiamare la recvfrom().

La prossima volta, prima di aprire un thread come questo, magari dovresti verificare che almeno le chiamate di sistema le hai fatte correttamente...

parisisalvo
22-11-2005, 22:54
Adesso provo, ma la cosa che non mi torna è :

a) xche funziona sulle altre macchine meno che la mia?
b) xche se tolgo/modifico istruzioni successive funziona?
c) xche se tolgo tutto il codice riguardante gtk funziona?
d) xche se come struttura metto sockaddr e non sockadd_in funziona?

Adesso provo a compilare, con sockaddr_in e impostando len a sizeof rem.
Io credevo che la recv andasse a modificare la len, e non che dovessi impostarla io...mi dispiace.
Grazie mille per l'aiuto!

Black imp
23-11-2005, 02:35
Qualcosa tipo:

len = sizeof rem;

prima di chiamare la recvfrom().

La prossima volta, prima di aprire un thread come questo, magari dovresti verificare che almeno le chiamate di sistema le hai fatte correttamente...


perchè, dà fastidio a qualcuno? da un errore come questo può darsi che altri quando si accingeranno a programmare questo tipo di task staranno attenti in virtù di questa discussione.

Qu@ker
23-11-2005, 18:41
perchè, dà fastidio a qualcuno?
Si.
Vedi, se uno da' un titolo come "Aiuto, ci non capisco nulla!" e' una cosa.
Se invece il titolo e' del tipo "Succede l'impossibile, i gremlins stanno mangiando i miei bytes?", e aggiunge che programma da N anni, io mi aspetto che le cose ovvie le abbia gia' verificate (tra l'altro, quando alla fine si scopre che ha passato un parametro con un valore casuale a una chiamata di sistema, l'autore non ci fa una gran figura).

Black imp
24-11-2005, 02:17
Si.
Vedi, se uno da' un titolo come "Aiuto, ci non capisco nulla!" e' una cosa.
Se invece il titolo e' del tipo "Succede l'impossibile, i gremlins stanno mangiando i miei bytes?", e aggiunge che programma da N anni, io mi aspetto che le cose ovvie le abbia gia' verificate (tra l'altro, quando alla fine si scopre che ha passato un parametro con un valore casuale a una chiamata di sistema, l'autore non ci fa una gran figura).


sì va bene ha sbagliato ma non è che fa un dispetto a te anche se a te indispettisce. scusa mi sembra un problema più tuo che suo, senza offesa.