[MISTERO] C corruzione della memoria

Area di discussione libera.

Moderatore: Staff

Regole del forum
1) Rispettare le idee altrui.
2) Evitare le offese dirette.
3) Leggere attentamente le risposte ricevute
4) Scrivere i messaggi con il colore di default, evitare altri colori.
5) Scrivere in Italiano o in Inglese, se possibile grammaticalmente corretto, evitate stili di scrittura poco chiari, quindi nessuna abbreviazione tipo telegramma o scrittura stile SMS o CHAT.
6) Appena registrati è consigliato presentarsi nel forum dedicato.

La non osservanza delle regole porta a provvedimenti di vari tipo da parte dello staff, in particolare la non osservanza della regola 5 porta alla cancellazione del post e alla segnalazione dell'utente. In caso di recidività l'utente rischia il ban temporaneo.
Mario Vanoni
Iper Master
Iper Master
Messaggi: 3174
Iscritto il: lun 3 set 2007, 21:20
Nome Cognome: Mario Vanoni
Slackware: 12.2
Kernel: 3.0.4 statico
Desktop: fluxbox/seamonkey
Località: Cuasso al Monte (VA)

Messaggio da Mario Vanoni »

lamarozzo ha scritto:
phobos3576 ha scritto:... lo standard ANSI C non prevede esplicitamente il controllo sui limiti degli array per cui sarebbe un controsenso introdurre una simile caratteristica (che appesantirebbe pure i programmi in C) .
phobos se ti rileggi i post ti accorgi che noi non stiamo parlando di nessun controllo run-time del compilatore C. Semplicemente un warning del compilatore (come fa icc) oppure un tool che ti aiuta a scovare le situazioni in cui leggi variabili statiche non inizializzate.
Programma _questo_ tool, e sarai celebre come K&R!

Mario Vanoni

Avatar utente
stan
Linux 3.x
Linux 3.x
Messaggi: 524
Iscritto il: lun 25 dic 2006, 11:55
Slackware: 12.1
Kernel: 2.6.xx
Desktop: Kde3
Località: Bs

Messaggio da stan »

Mario Vanoni ha scritto: Programma _questo_ tool, e sarai celebre come K&R!
Non sempre si diventa celebri per (o solo) un'idea. Ma questo lo sappiamo tutti.

Avatar utente
nuitari
Linux 3.x
Linux 3.x
Messaggi: 777
Iscritto il: dom 14 ott 2007, 12:51
Slackware: 12.0
Località: San Colombano al Lambro
Contatta:

Messaggio da nuitari »

sir_alex ha scritto:bla bla bla
Nego tutto, ovviamente.
Dato che a quanto pare non hai letto i miei esempi, te ne posto un altro. Chissà che magari capirai che hai detto una stupidaggine.

Codice: Seleziona tutto

#include "stdio.h"
#include "stdlib.h"

#define ARRAY_DIM 10

int
main (void)
{
        int *array = (int *)malloc(sizeof(int) * ARRAY_DIM);
        int iterator = 0;

        for (iterator = 0; iterator < ARRAY_DIM; iterator++)
        {
                array[iterator] = iterator;
                printf("array[%i] = %i\n", iterator, array[iterator]);
        }

        free(array);
        return 0;
}
output:

Codice: Seleziona tutto

samuele@nuitari-laptop:~$ gcc check.c && a.out
array[0] = 0
array[1] = 1
array[2] = 2
array[3] = 3
array[4] = 4
array[5] = 5
array[6] = 6
array[7] = 7
array[8] = 8
array[9] = 9
OMMIODDIO, ho usato le parentesi quadre per accedere ad una zona di memoria definita arbitrariamente da me come se fosse un array deferenziando un puntatore, e NON PER CREARE UN ARRAY!!!!!!

Sarà un bug di Matrix? Sarà che dovrei sostituire il tuo prof di Informatica? Sarà che non basta un corso d'Informatica 1 per pretendere di conoscere l'informatica?

Ai posteri l'ardua sentenza.

PS: Per inciso, in Java gli array sono oggetti gestiti dal linguaggio e quindi un tipo dati, in C++ non esistono come in C ma si usano oggetti della STL come i vector o le map ad esempio, e quindi nuovamente sono oggetti e funzionano da tipi dati (le classi).
Veramente, studia...
Ultima modifica di nuitari il ven 14 dic 2007, 22:07, modificato 1 volta in totale.

Avatar utente
nuitari
Linux 3.x
Linux 3.x
Messaggi: 777
Iscritto il: dom 14 ott 2007, 12:51
Slackware: 12.0
Località: San Colombano al Lambro
Contatta:

Messaggio da nuitari »

Altro esempio per dimostrare la grossa castroneria che hai detto ed il reale funzionamento delle parentesi quadre:

Codice: Seleziona tutto

#include "stdio.h"

struct tre_numeri {
        int a;
        int b;
        int c;
};

int
main (void)
{
        struct tre_numeri prova = {0};
        int *puntatore = (int *)&prova;
        puntatore[0] = 1;
        puntatore[1] = 2;
        puntatore[2] = 3;

        printf("prova.a = %d\nprova.b = %d\nprova.c = %d\n", prova.a, prova.b, prova.c);

        return 0;
}
Output:

Codice: Seleziona tutto

samuele@nuitari-laptop:~$ gcc -Wall check3.c && a.out
prova.a = 1
prova.b = 2
prova.c = 3
Per inciso questo codice funziona anche in C++ con gli oggetti, considerato che la memoria occupata da un oggetto è sempre contigua ed è uguale alla somma della dimensione delle sue variabili membro.

Avatar utente
robbybby
Linux 4.x
Linux 4.x
Messaggi: 1223
Iscritto il: sab 16 dic 2006, 10:48
Slackware: 13.1 / 64 bit
Kernel: 3.3.x
Desktop: KDE 4.4.5
Località: Fra Trantor e Terminus

Messaggio da robbybby »

Quoto nuitari e suggerisco di (ri)leggere il capitolo 5.3 del K&R.
Ne riporto solo una riga:

Codice: Seleziona tutto

"Ogni operazione che puo' essere ottenuta dall'indicizzazione di un array, puo' anche essere ottenuta coi puntatori."
E ancora (e forse e' piu' l'oggetto del contendere):

Codice: Seleziona tutto

"se pa e' un puntatore, le espressioni possono utilizzarlo come un indice: pa[i] eì identico a *(pa+i). In breve, ogni espressione array ed indice puo' essere scritta come un puntatore a valore di scarto e VICEVERSA [il maiuscolo e' mio].

Mario Vanoni
Iper Master
Iper Master
Messaggi: 3174
Iscritto il: lun 3 set 2007, 21:20
Nome Cognome: Mario Vanoni
Slackware: 12.2
Kernel: 3.0.4 statico
Desktop: fluxbox/seamonkey
Località: Cuasso al Monte (VA)

Messaggio da Mario Vanoni »

@nuitari

Nei sai piu` di me, grazie, non finiro` mai d'imparare.

@robbybby

Grazie per la precisazione.

Mario Vanoni

Avatar utente
puzuma
Linux 2.x
Linux 2.x
Messaggi: 482
Iscritto il: mar 4 lug 2006, 17:14
Nome Cognome: Stefano Salvador
Slackware: current
Kernel: 2.6.32.2
Desktop: KDE 4.4.0
Località: Udine
Contatta:

Messaggio da puzuma »

noto con piacere che avete ignorato il fatto che scrivere oltre i limiti di un array si chiama "buffer overflow" e che è visto come il male praticamente ovunque.

cercherò di non usare mai i programmi scritti da voi, ci tengo non avere exploit sul mio computer.

klose87
Linux 0.x
Linux 0.x
Messaggi: 66
Iscritto il: gio 1 mar 2007, 22:22
Slackware: 13.37
Kernel: 2.6.37.6
Desktop: KDE4
Località: Cannara (PG)

Messaggio da klose87 »

Si parla di C, ma i 2 schieramenti contrapposti sono un classico che dura nel tempo: veterani vs. apprendisti. :)
Mi sono appassionato alla discussione perché appartengo ad una terza categoria: quella dei dilettanti allo sbaraglio. Mi sono avvicinato seriamente al C da appena un mese (causa università), devo dire che mi sta veramente intrigando.
Non prendetela come una provocazione, è una domanda seria che mi è venuta leggendo qui: se riservo 7 zone di memoria, ci sono dei casi pratici in cui ho benefici nel controllare l'ottava? Mi rendo conto che potrebbe essere una domanda priva di senso e/o inutile, in tal caso fatemelo notare...

E ora... l'angolo del moralismo!
Non potreste cercare di abbassare un pochino i toni della discussione? Nelle ultime 4 pagine del thread ho visto (da entrambe le parti) diversa gente rispondere a colpo sicuro senza aprire minimamente al contendente. C'è modo e modo per esprimere un'opinione. Anche quando si è sicuri al 500% di avere ragione, non lo si può far pesare agli altri, e non serve dire cose tipo "è così, punto e basta". Dall'altra parte, non si può continuare a pretendere di avere ragione di fronte all'evidenza. Cerchiamo di non degenerare per così poco! :D

Avatar utente
phobos3576
Staff
Staff
Messaggi: 2980
Iscritto il: dom 17 apr 2005, 0:00
Slackware: 13.1
Kernel: 2.6.37-smp
Desktop: KDE 4.5.3

Messaggio da phobos3576 »

puzuma ha scritto:noto con piacere che avete ignorato il fatto che scrivere oltre i limiti di un array si chiama "buffer overflow" e che è visto come il male praticamente ovunque.

cercherò di non usare mai i programmi scritti da voi, ci tengo non avere exploit sul mio computer.
Il buffer overflow è una tecnica applicata da persone in malafede e consiste nel prendere il controllo di un qualche puntatore che indirizza un buffer di memoria in un programma; a quel punto si può sfruttare il puntatore per immettere dati anche oltre la lunghezza del buffer.
Quei dati possono rappresentare, ad esempio, il codice macchina di un qualche virus o di un qualche comando che permette di prendere il controllo del PC.

Invece, uno sconfinamento involontario dai limiti di un array, male che vada provoca un crash del relativo programma o, al massimo, un segmentation fault; se però un cracker riesce a prendere il controllo di un puntatore ad un buffer, hai voglia di mettere controlli sui limiti del buffer stesso!
In tal caso, infatti, il vero problema è la vulnerabilità del programma e non tanto i limiti del buffer.

Avatar utente
nuitari
Linux 3.x
Linux 3.x
Messaggi: 777
Iscritto il: dom 14 ott 2007, 12:51
Slackware: 12.0
Località: San Colombano al Lambro
Contatta:

Messaggio da nuitari »

klose87 ha scritto:Si parla di C, ma i 2 schieramenti contrapposti sono un classico che dura nel tempo: veterani vs. apprendisti. :)
Mi sono appassionato alla discussione perché appartengo ad una terza categoria: quella dei dilettanti allo sbaraglio. Mi sono avvicinato seriamente al C da appena un mese (causa università), devo dire che mi sta veramente intrigando.
Non prendetela come una provocazione, è una domanda seria che mi è venuta leggendo qui: se riservo 7 zone di memoria, ci sono dei casi pratici in cui ho benefici nel controllare l'ottava? Mi rendo conto che potrebbe essere una domanda priva di senso e/o inutile, in tal caso fatemelo notare...
Diciamo che dipende da come stai gestendo la memoria.
Se in tutto l'applicativo inizializzi solo quelle 7 ipotetiche zone di memoria, non le riallochi mai etc etc, allora la risposta è no, chiaramente no.
Il fatto è che questo il compilatore non lo può sapere, perchè l'inizializzazione della memoria ed il suo utilizzo dipendono dal codice eseguito in runtime, codice che può essere estremamente articolato e complesso, variare in base all'input dell'utente, allo stato delle periferiche, etc etc etc. Non è possibile *simulare* ogni pattern in fase di compilazione di un programma, è semplicemente irrealizzabile. Realizzarla parziale giusto per avere un warning sarebbe, IMHO, una drammatica perdita di tempo poichè coprirebbe un caso su... un triliardo? E soprattutto fornirebbe la scusa per non scrivere/usare codice necessario a gestire gli errori quando invece è obbligatorio farlo.

Es.: Supponi del codice di questo tipo:

Codice: Seleziona tutto

#include "stdio.h"

int
main (void)
{
        int giorni[30] = {0};
        int i = 0;
        for (i = 0; i < 31; i++)
        {
                if (i % 2 == 1)
                {
                        printf("%d ", i);
                        giorni[i] = 1;
                }
        }
        printf("\ndone.\n");

        return 0;
}
"giorni" è composto da 30 elementi, numerati da 0 a 29.
Il ciclo for prosegue da 0 a 30, per cui ipoteticamente "giorni[ i ] = 1" potrebbe causare un errore di violazione della memoria.
In realtà questo non accadrà mai perchè if (i % 2 == 1) gli farà considerare solo gli elementi dispari, per cui l'ultimo ad essere processato è il 29.

Output:

Codice: Seleziona tutto

samuele@nuitari-laptop:~$ gcc check4.c && a.out
1 3 5 7 9 11 13 15 17 19 21 23 25 27 29
done.
Domanda: come potrebbe mai un compilatore capire una cosa del genere??? Risposta: non può. E qui è semplice... potrebbe essere MOLTO più complesso di così.

L'unico tipo di controllo sulla memoria è quello fornito dal kernel, ma è un discorso di natura diversa e comunque avvisa solo che c'è stata una violazione e da eventualmente le informazioni utili a debuggare, ma di certo non comunica in quale riga del listato originale è avvenuto l'errore.

Il punto è che quando si lavora con la memoria in modo diretto, senza passare da funzioni d'interfaccia studiate ad hoc (come ad esempio una classe per la gestione di vettori), è tutto in mano al programmatore.
Il C mette a disposizione solo operazioni con i puntatori, che siano espliciti definendo variabili di tipo puntatore od impliciti utilizzando operatori unari come "*" o "&" o binari come le parentesi []. Per gestire strutture di dati complesse, come ad esempio gli array, è necessario scrivere del codice ad hoc SE si desidera che il programma non termini con un errore non gestito, dato che comunque errare humanum est.
La stessa inizializzazione di un gruppo di variabili con le parentesi [] è limitante. Ad esempio la dimensione non è ridefinibile in runtime, cosa che invece può accadere con malloc. Il motivo è, ancora una volta, che le parentesi [] non sono state pensate per la gestione dell'"oggetto array", un concetto di ben più alto livello.

@Mario Vanoni: :oops: Nessuno finisce mai d'imparare, io per primo =)

Avatar utente
lamarozzo
Linux 3.x
Linux 3.x
Messaggi: 732
Iscritto il: gio 14 lug 2005, 0:00
Desktop: xfce
Distribuzione: archlinux
Località: Roma

Messaggio da lamarozzo »

Mario Vanoni ha scritto: Programma _questo_ tool, e sarai celebre come K&R!
Purtroppo non sono in grado, comunque l'hanno già fatto i programmatori intel :)
nuitari ha scritto: Domanda: come potrebbe mai un compilatore capire una cosa del genere??? Risposta: non può. E qui è semplice... potrebbe essere MOLTO più complesso di così.
Infatti non mi aspetto che sia il compilatore a farlo (icc è stata una sorpresa a dir la verità). Valgrind funziona proprio così. Lui esegue il tuo programma in un ambiente leggermente diverso, in cui ad ogni zona di memoria associa una flag che indica se la zona è stata inizializzata oppure no. Se leggi il valore di una zona di memoria non inizializzata lui ti segnala il probabile errore.
Come vedi questo metodo copre bene anche il caso che hai codificato tu perchè l'errore viene sollevato solo se effettivamente utilizzi una variabile non inizializzata.
Però, come ho fatto vedere qualche post fa, Valgrind si può ingannare. Io crea un array statico di dimensioni 8. Lo standard mi dice che verrà inizializzato a zero. Però poi quando accedo all'elemento 9 Valgrind non mi segnala nulla perchè evidentemente il compilatore ha pensato bene di inizializzare una zona di memoria più estesa di quanto gli avessi chiesto (non penso che sia una richiesta dello standard questa). Ora bisogna solo incrociare le dita e sperare che il compilatore non assegni la zona di memoria corrispondente al nono elemento ad un'altra variabile, altrimenti succede quello che è successo a me. Corruzione della memoria e Valgrind muto! :(

Avatar utente
nuitari
Linux 3.x
Linux 3.x
Messaggi: 777
Iscritto il: dom 14 ott 2007, 12:51
Slackware: 12.0
Località: San Colombano al Lambro
Contatta:

Messaggio da nuitari »

Lo ripeto: l'errore è alla base, perchè stai usando un elemento del linguaggio per qualcosa che non gli compete. Il problema che devi porti è diverso, tutto li.

Inoltre: il punto è che non puoi scrivere codice insicuro e sperare che funzioni. Valgrind simulerà, testerà.. ma i bug arrivano nei momenti meno aspettati secondo i pattern più impensabili. Per questo devi fare opera di buona programmazione e farti funzioni etc per gestire errori, array e compagnia briscola. Perchè non puoi prevedere l'imprevedibile.

Avatar utente
puzuma
Linux 2.x
Linux 2.x
Messaggi: 482
Iscritto il: mar 4 lug 2006, 17:14
Nome Cognome: Stefano Salvador
Slackware: current
Kernel: 2.6.32.2
Desktop: KDE 4.4.0
Località: Udine
Contatta:

Messaggio da puzuma »

sono daccordo con le conclusioni di nuitari, scrivere codice sicuro dev'essere la priorità per chi programma in c proprio a causa della sua potenza. Lasciatemi però sperare che quando ho fatto tutto per evitarlo ma compare un bug esista un tool che almeno in modo imperfetto mi possa aiutare a scovarlo in modo rapido.

@phobos3576

il buffer overflow non è quello che dici tu, è un errore del programmatore che va scrivere in area di memoria in modo non intenzionale, poi un cracker può tentare di sfruttarlo.

il motivo del contendere era proprio che se per evitare questo caso si possa usare un qualche tipo di tool automatico, ovviamente questo tool non esiste e leggendo il succitato articolo della wikipedia pare che tutti quelli tentati siano imperfetti lasciando il programmatore solo con il suo codice.

Avatar utente
conraid
Staff
Staff
Messaggi: 13631
Iscritto il: gio 14 lug 2005, 0:00
Nome Cognome: Corrado Franco
Slackware: current64
Desktop: kde
Località: Livorno
Contatta:

Messaggio da conraid »

puzuma ha scritto:
@phobos3576

il buffer overflow non è quello che dici tu, è un errore del programmatore che va scrivere in area di memoria in modo non intenzionale, poi un cracker può tentare di sfruttarlo.
Valgono entrambe le cose. Spesso i buffer overflow sono creati ad arte, per scopi più o meno "salutari" :-)
Comunque non vedo il problema, non penso che cambi la sua definizione se sia stata l'imperizia o la furbizia del programmatore a farlo

sir_alex
Linux 3.x
Linux 3.x
Messaggi: 735
Iscritto il: lun 21 mar 2005, 0:00
Kernel: 2.6.35-22
Desktop: KDE4
Distribuzione: Ubuntu
Località: Milano - Corbola (RO)
Contatta:

Messaggio da sir_alex »

Ok, chiudo il mio intervento in relax: avevo premesso in origine di non avere manuali di C, quindi aver citato K&R è stato sicuramente utile come fonte autorevole; il succo del mio intervento era semplicemente di non dire che [] sono operatori che non si devono utilizzare per creare array, dato che sono stati "creati" dagli autori del linguaggio apposta per fare quello, in maniera più semplice che gestire puntatori ed utilizzare l'aritmetica di questi.
E' alla fine chiaro ed evidente che il comportamento risulta identico, non ho mai smentito questo, solo che secondo me non c'è motivo di dire che non si utilizzano per quello scopo...
Per il discorso Java, sì sono oggetti (infatti un array lo si inizializza con "new"), qui almeno concordiamo... :-)

Rispondi