[so] Case study - Optiunea -fpack-struct

Stefan Bucur stefan.bucur at gmail.com
Sun Dec 9 21:17:21 EET 2007


Salut!

Intrucat am pierdut o groaza de vreme cu problema asta, consider ca
merita s-o postez si pe lista de discutii, pentru ca altii sa invete
din greselile mele si sa le evite pe viitor.

Dupa cum multi dintre voi stiu, GCC-ul aloca structurile C aliniind
campurile intr-un mod cat mai optim pentru masina pe care se
compileaza codul respectiv. In mod concrent, de obicei campurile
structurii sunt aliniate la 32 de biti (pentru x86), creand "gauri" in
spatiul de memorie ocupat de structura.

Intrucat protocolul meu de comunicare dintre client si server pentru
tema 4 se bazeaza pe structuri C, am zis ca ar fi bine sa eficientizez
cantitatea de date transmisa si sa comprim structurile in memorie.
Pentru asta, stiam ca exista optiunea de compilare -fpack-struct, care
facea fix ce voiam eu pentru toate structurile, fara sa fiu nevoit sa
adaug __attribute__((__packed__)) pentru fiecare structura de-a mea in
parte.

Cu toate acestea, conform legii nescrise cum ca "lenesul mai mult
alearga", optiunea asta mi-a adus o groaza de necazuri. La un moment
dat, programul meu incepea sa-mi dea total aiurea Segmentation fault.
Dupa analize minutioase cu GDB-ul, ajunsesem la concluzia ca functia
de sistem stat() imi facea un buffer overflow in parametrul de tip
struct stat (!!!!!).

Dupa vreo ora de verificari si rasverificari, eram ferm convins ca am
gasit un bug in functia stat() [ you may laugh at this point :P ], asa
ca am pregatit un program de test care sa ilustreze acest bug, si pe
care sa-l raportez mai departe:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <stdio.h>

struct stat_test {
        struct stat statbuf;
        int testint;
} __attribute__((__packed__));

int main(int argc, char **argv) {
        struct stat_test st;
        st.testint = 0xFFFFFFFF;

        char file[4096]="/usr/bin/tail";

        printf("Before: %d\n", st.testint);

        if (stat(file, &st.statbuf) == -1) {
                perror("stat");
                return 1;
        }

        printf("After: %d\n", st.testint);
        return 0;
}

Insa atunci cand am compilat si rulat programul, surpriza! Nu mai avea
loc nici un overflow. Dupa cateva minute de analize si comparatii,
mi-am dat seama intr-un final care era problema:

Parametrul -fpack-struct, in mod evident, impacheta si structurile din
headerele de sistem (pentru ca erau incluse in fisierul sursa la
compilare), generand cod binar incompatibil cu biblioteca C. Acest
lucru este dealtfel subliniat si in pagina de manual a GCC-ului, pe
care nu binevoisem s-o citesc de la inceput. :) Codul meu de test nu
fusese compilat cu aceasta optiune; imediat ce am adaugat optiunea,
overflow-ul a fost vizibil.

Concluzia: Atunci cand aveti nevoie sa impachetati structurile in C,
evitati sa folositi parametrul de compilare -fpack-struct, ci folositi
in schimb __attribute__((__packed__)) pentru fiecare structura pe care
doriti s-o impachetati.

Numai bine,

Stefan Bucur


More information about the so mailing list