Προς το περιεχόμενο

Προτεινόμενες αναρτήσεις

Δημοσ.

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libconfig.h>
#include <libusb.h>

struct pam_usb_device {
    int    vendor;
    int    product;
    char  *serial;
};

struct pam_usb_user {
    char  *login;
    size_t ndevices;
    struct pam_usb_device devices[0];
};

struct pam_usb_conf {
    size_t nusers;
    struct pam_usb_user **users;
};

int read_conf(struct pam_usb_conf **conf, const char *config_file) {
    struct pam_usb_conf *res;
    config_setting_t *users, *devices, *user, *device;
    config_t cfg, *cf;
    int i, j;
    char *msg = NULL;
    const char *tmp;

    cf = &cfg;
    config_init(cf);
    if(!config_read_file(cf, config_file)){
        fprintf(stderr, "%s:%d - %s\n", config_error_file(cf), config_error_line(cf), config_error_text(cf));
        config_destroy(cf);
        return 0;
    }

    if((users = config_lookup(cf, "users")) == NULL){
        msg = "no users in config"; goto error;
    }

    int nusers = config_setting_length(users);
    res = malloc(sizeof(struct pam_usb_conf) + (nusers * sizeof(struct pam_usb_user *)));
    if(res == NULL){
        msg = "malloc for pam_usb_conf failed"; goto error;
    }
    res->nusers = nusers;

    for(i = 0; i < nusers; i++){
        if((user = config_setting_get_elem(users, i)) == NULL) continue;
        if((devices = config_setting_get_member(user, "devices")) == NULL){
            msg = "no devices for user"; goto error;
        }
        int ndevices = config_setting_length(devices);
        res->users[i] = malloc(sizeof(struct pam_usb_user) + (ndevices * sizeof(struct pam_usb_device)));
        res->users[i]->ndevices = ndevices;
        if(config_setting_lookup_string(user, "login", &tmp) == CONFIG_FALSE){
            msg = "no login name specified"; goto error;
        }
        res->users[i]->login = strdup(tmp);
        for(j = 0; j < ndevices; j++){
            if((device = config_setting_get_elem(devices, j)) == NULL) continue;
            if(config_setting_lookup_int(device, "vid", &res->users[i]->devices[j].vendor) == CONFIG_FALSE){
                msg = "no device vendor specified"; goto error;
            }
            if(config_setting_lookup_int(device, "pid", &res->users[i]->devices[j].product) == CONFIG_FALSE){
                msg = "no device product specified"; goto error;
            }
            if(config_setting_lookup_string(device, "serial", &tmp) == CONFIG_FALSE){
                msg = "no device serial specified"; goto error;
            }
            res->users[i]->devices[j].serial = strdup(tmp);
        }
    }

    config_destroy(cf);
    *conf = res;
    return 1;

error:
    if(msg) fprintf(stderr, "%s: %s", config_error_file(cf), msg);
    config_destroy(cf);
    return 0;
}


int main(){
    struct pam_usb_conf *cfg;
    int i, j;
    libusb_context *context = NULL;
    libusb_device **lst = NULL;
    int rc = 0;
    ssize_t count = 0;
    size_t idx = 0;

    rc = libusb_init(&context);
    if (rc < 0) printf("error\n");

    count = libusb_get_device_list(context, &lst);

    for (idx = 0; idx < count; ++idx) {
        libusb_device *device = lst[idx];
        struct libusb_device_descriptor desc = {0};

        rc = libusb_get_device_descriptor(device, &desc);

        printf("Vendor:Device = %04x:%04x\n", desc.idVendor, desc.idProduct);
    }

    if(read_conf(&cfg, "config.cfg")){
        for(i = 0; i < cfg->nusers; i++){
            printf("%s\n", cfg->users[i]->login);
            for(j = 0; j < cfg->users[i]->ndevices; j++){
                struct pam_usb_device *device = &cfg->users[i]->devices[j];
                printf("\t%04x %04x %s\n", device->vendor, device->product, device->serial);
            }
        }
        free(cfg);
    }


    return 0;
}

Έχω αυτό το πρόγραμμα το οποίο δουλεύει αλλά αν φέρω τη βιβλιοθήκη libusb κάτω από το read_conf τότε μου πετάει segmentation fault στη γραμμη get_usb_device_list

 

o gdb δίνει

 

 

#0  0x00007ffff7678521 in _int_malloc () from /lib64/libc.so.6

#1  0x00007ffff767a26c in malloc () from /lib64/libc.so.6

#2  0x00007ffff79bf78c in libusb_get_device_list () from /lib64/libusb-1.0.so.0

#3  0x000000000040111d in main () at main.c:112

 

To οποίο δε βγάζει νόημα.

 

Ένα sample .cfg

 

users = (

    {

        login = "user";

        devices = ({

            vid     = 0x1234;

            pid     = 0x1100;

            serial  = "34432374237";

        });

    },

    {

        login = "user2";

        devices = (

            {

                vid     = 0x1234;

                pid     = 0x1100;

                serial  = "34432374237";

            },

            {

                vid     = 0x5678;

                pid     = 0x0011;

                serial  = "123123123123";

            }

        );

    }

);

 

Καμιά ιδέα τι μπορεί να φταίει?

Δημοσ.

Το context που το χρησιμοποιείς; Δεν σου χρειάζεται πουθενα. Απλα κανεις init την βιβλιοθήκη με NULL.

Δες εδω ενα παραδειγμα με το libusb που απλα ελέγχει αν μια συσκευή USB είναι συνδεδεμένη στο PC, δεδομένου του vendor ID και του device ID της. Ενα αλλο μικροπρογραμματακι που ειχα κανει αποθηκευει τις συσκευες σε μια απλα συνδεδεμενη λιστα.

 

Επίσης το libconfig είναι καπως περιπλοκο για ενα τοσο απλο .conf αρχειο σαν το δικο σου. Θα σου συνιστουσα να κοιταξεις το libconfuse (ειναι πακετο σε διανομες linux) που ειναι παρα πολυ απλο στην χρηση του. Το ειχα χρησιμοποιήσει σε ενα project στα windows και δες ποσο ευκολο ειναι στην χρηση του ουτε loops ουτε τιποτα. Αρκει βεβαια το .conf αρχειο σου να εχει standard layout. Αλλιως καλα κανεις και χρησιμοποιείς libconfig

Δημοσ.

Δεν διαφωνω καπου, ετσι κι αλλιως τον κωδικα της libusb τον πηρα απο παραδειγμα, το θεμα ειναι οτι αδυνατω να βρω τι φταιει και εχει θεμα με τη μνημη. Ευχαριστώ για τα παραδείγματα πάντως.

Δημοσ.

Δε μου διορθωνει το πρόβλημα όμως. Και δε μπορώ να καταλάβω που ειναι το πρόβλημα, το οποίο προφανώς προέρχεται απο την read_conf

Δημοσ.

Επειδή δεν είναι εκ πρώτης όψεως πολύ λογικό αυτό που σου συμβαίνει, χωρίς να ξέρω και χωρίς να έχω μελετήσει τι ακριβώς γίνεται, μια ιδεά απλά:

 

Φαίνεται πως στο stack στη main έχεις πρώτα το struct pam_usb_conf *cfg και μετά διάφορα πράγματα που χρησιμοποιεί η libusb. Περνάς τον cfg στην άλλη function. Αν κάπου κάπως γίνει κάποιο overrun εκει μέσα το πιθανότερο είναι ότι ο κώδικας που φταίει θα γράψει πάνω στις επόμενες μεταβλητές μέσα στο stack frame. Με 10 μόνο bytes overrun έχεις φτάσει να γράφεις πάνω στην context και να της αλλάζεις την τιμή από NULL, πράγμα που δε νομίζω να αρέσει στην libusb.

 

Βάλε πρώτα τη read_conf και αμέσως μετά το if βάλε breakpoint να δεις αν τo context είναι ακόμα NULL. Αν όχι, συγχαρητήρια κάπου μέσα στη read_conf γίνεται overrun.

  • Like 1
Δημοσ.

Κατσε περιμενε, το προβλημα δεν γινεται οταν καλεις την libusb_get_device_list(); Αυτο δεν λες στο αρχικο σου ποστ;

Δες με τον gdb σε ποια γραμμη γινεται Segfault.

 

Το εριξα μια μικρη ματια ομως και ειδα αυτο

res->users[i] = malloc(sizeof(struct pam_usb_user) + (ndevices * sizeof(struct pam_usb_device)));

Το

res->users[i]

ειναι τυπου

struct pam_usb_user *

Αρα θα κανεις

res->users[i] = malloc(sizeof(struct pam_usb_user));

Θα σε συμβουλευα να μην κανεις εκτενη χρηση τετοιων δυναμικων πινακων. Αν θες δυναμικο πινακα χρησιμοποιησε απλο pointer και βγαζε τα μελη του πινακα σαν offsets. Αν καταλαβα καλα ομως με αυτο που πας να κανεις, το παραπανω δεν σε βοηθαει. Οριζεις την δομη

struct pam_usb_user {
   char *login;
   size_t ndevices;
   struct pam_usb_device devices[0];
};

Με το τελευταιο μερος πινακα [0] για να μπορεις να το αρχικοποιησεις οπως θελεις δυναμικα, αλλα δεν δουλευει ετσι οπως περιμενεις. Ειναι πολυ κακη πρακτικη γενικα αυτο που πας να κανεις και δεν χρησιμοποιεις σωστα την τεχνικη ετσι κι αλλιως. Το κανεις και εδω

res = malloc(sizeof(struct pam_usb_conf) + (nusers * sizeof(struct pam_usb_user *)));

Εχω δει παρομοιο κωδικα και στον πυρηνα του Linux (με δομες που τελειωνουν σε [0]).

Κανε το τελευταιο μελος της δομης εναν δεικτη και δοκιμασε κατι τετοιο:

struct pam_usb_conf {
    size_t nusers;
    struct pam_usb_user **users;
};

struct pam_usb_user {
   char *login;
   size_t ndevices;
   struct pam_usb_device *devices;
};

.
.
.

res = malloc(sizeof(struct pam_usb_conf));

.
.
.

res->users[i] = malloc(sizeof(struct pam_usb_user));
res->users[i]->devices = malloc(ndevices * sizeof(struct pam_usb_device)));

Edit: Ξεχασα να πω, το res->users->devices γίνεται δυναμικος πινακας με το παραπανω malloc αρα θα επεξεργαζεσαι τα μελη του με αυτην την συνταξη

res->users[i]->devices[i].structmember
Δημοσ.

Με τον gdb γίνεται seg fault στην get_usb_list_device, απλα αν τη βαλεις πριν το read_conf δεν εχεις θεμα αν τη βαλεις μετα εχεις.

Αν θέλω να τον κανω δυναμικό τότε θέλω **devices

Δημοσ.

Βαζεις ολες τις μεταβλητες της main στο watch. Και μετα πας στεπ μπαι στεπ την ριντ κονφιγκ μεχρι να δεις να αλλαζει κατι απο τα waches που δεν θα επρεπε να αλλαξει.

Δημοσ.

Με τον gdb γίνεται seg fault στην get_usb_list_device, απλα αν τη βαλεις πριν το read_conf δεν εχεις θεμα αν τη βαλεις μετα εχεις.

Αν θέλω να τον κανω δυναμικό τότε θέλω **devices

 

Δυναμικούς πίνακες στην C φτιάχνεις με μονούς δείκτες. Για παράδειγμα ένας δυναμικός πίνακας ακεραίων 10 θέσεων θα δούλευε έτσι:

int *arrInt = malloc(sizeof(int) * 10);

arrInt[0] = 43;
arrInt[1] = 4444444;

Αν χρησιμοποιήσεις διπλούς δείκτες (δηλαδή πίνακα σε δείκτες) θα πρέπει να αρχικοποιήσεις και όλες τις θέσεις του πίνακα αυτού με malloc. Ο αντίστοιχος κώδικας από παραπάνω δηλαδη θα γινόταν έτσι:

int *arrInt = malloc(sizeof(int*) * 10);

arrInt[0] = malloc(sizeof(int));
*arrInt[0] = 43;

arrInt[1] = malloc(sizeof(int));
*arrInt[1] = 4444444;

Έτσι δουλεύουν οι πίνακες σε δείκτες. Εσύ προσπάθησες να προσπεράσεις την παραπάνω διαδικασία χρησιμοποιώντας ένα compiler extension (το [0] στο τελος του struct) αλλα κατα πασα πιθανοτητα έπεσες σε overflow όπως ειπε και ο Defacer παραπανω, με αποτέλεσμα να δημιουργείται πρόβλημα στο πρόγραμμά σου, κάθε φορά που καλείς την συνάρτηση.

 

Θα σου συνιστούσα να αφαιρέσεις τα [0] και να το κάνεις σωστά με δείκτες.

 

Edit: Επίσης γενικά να αποφεύγετε να χρησιμοποιείτε κώδικα της μορφής:

int len;

scanf("%d", &len);

int dynArr[len];

Οι δυναμικοί πίνακες στην C δεν είναι σαν τα dictionaries στην Python ή σαν τα dynamic arrays στην Java. Είναι εξαιρετικά επιρρεπή σε stack overflow και γενικά είναι ορολογιακή βόμβα.

 

Στην περίπτωσή σου, ήσουν τυχερός που έπεσες σε segfault καθώς το σφάλμα ήταν αθόρυβο. Ο μόνος λόγος που έπεσες σε segfault ήταν επειδή λόγω του overflow έβγαλε προβλήματα η συνάρτηση του libusb. Τα heap overflows (που υποθέτω πως είναι και το δικό σου) είναι απο τα πιο επικίνδυνα είδη overflow καθώς δεν σου στέλνει σήμα το λειτουργικό όταν συμβαίνουν, σε αντίθεση με τα stack overflow.

  • Like 1
Δημοσ. (επεξεργασμένο)

 

struct pam_usb_conf {
    size_t nusers;
    struct pam_usb_user **users;
};

int read_conf(struct pam_usb_conf **conf, const char *config_file) {
    int nusers = config_setting_length(users);
    res = malloc(sizeof(struct pam_usb_conf) + (nusers * sizeof(struct pam_usb_user *)));

    for(i = 0; i < nusers; i++){
        res->users[i] = malloc(sizeof(struct pam_usb_user) + (ndevices * sizeof(struct pam_usb_device)));
    }
}
Έχω αυτό το πρόγραμμα το οποίο δουλεύει αλλά αν φέρω τη βιβλιοθήκη libusb κάτω από το read_conf τότε μου πετάει segmentation fault στη γραμμη get_usb_device_list

 

o gdb δίνει

 

To οποίο δε βγάζει νόημα.

 

 

Δε μου διορθωνει το πρόβλημα όμως. Και δε μπορώ να καταλάβω που ειναι το πρόβλημα, το οποίο προφανώς προέρχεται απο την read_conf

Με τον gdb γίνεται seg fault στην get_usb_list_device, απλα αν τη βαλεις πριν το read_conf δεν εχεις θεμα αν τη βαλεις μετα εχεις.

Αν θέλω να τον κανω δυναμικό τότε θέλω **devices

Δοκίμασες να κάνεις αυτό που σου έδειξε ο gikoskos ?

 

Οι δείκτες είναι λίγο μπερδευτικό κεφάλαιο οπότε διάβασε ένα καλό βιβλίο για να κατανοήσεις κάποιες έννοιες που θα σε βοηθήσουν να μην μπερδεύεσαι. Δεν έχουμε δηλαδή στο μυαλό μας "δυναμικός == **" και τελειώσαμε αλλά πρέπει να κατανοήσουμε τι θα πει το κάθε * και πόσα χρειαζόμαστε.

 

Επίσης πολύ σημαντικό είναι να χρησιμοποιείς όσο πιο απλό κώδικα μπορείς και όχι φανταχτερά κόλπα όπως το struct hack, κτλ αν δεν τα χρειάζεσαι.

 

Εκχωρείς για την res, μνήμη για τη δομή και ακόμη ένα ποσό για nusers αριθμό από pam_usb_user. Υποθέτω ότι το κάνεις για το μέλος users της δομής. Η μεταβλητή σου όμως είναι ** οπότε τι θα κερδίσεις με αυτό το malloc ? Επίσης, μετά από αυτό πας και εκχωρείς πάλι μνήμη για μία δομή pam_usb_user στην κάθε res->users.

 

Δηλαδή έχεις μπλέξει πολλά πράγματα και ο κώδικάς σου είναι υπέρ του δέοντος πολύπλοκος χωρίς λόγο. Θα μου πεις γιατί παίζει όταν το βάλεις μετά την libusb ? Γιατί παράγεται κώδικας που εκχωρεί κάποιο διαφορετικό ποσό μνήμης και έτσι κάποια λάθος προσπέλαση που κάνεις τυγχάνει να παίζει.

 

Άλλαξα τον κώδικά σου ώστε να είναι πιο απλός του στυλ

res = malloc(τάδε)
res->users = malloc(τάδε)
κτλ
και παίρνω το ίδιο ακριβώς αποτέλεσμα χωρίς segmentation fault ανεξάρτητα από το που είναι η read_conf.

 

Edit:

 

Ας κάνουμε ένα poor-man's-debug με printf για να σου γίνει πιο κατανοητό που είναι το λάθος σου.

 

    int nusers = config_setting_length(users);

    printf("Prin to malloc o res deixnei se %p\n",(void *)res);
    int ll = sizeof(struct pam_usb_conf) + (nusers * sizeof(struct pam_usb_user *));
    res = malloc(sizeof(struct pam_usb_conf) + (nusers * sizeof(struct pam_usb_user *)));
    
    printf ("zhthsa apo thn malloc %d bytes gia thn res kai tora deixnei sthn %p\n", ll, (void *)res);
    printf("as doyme pou deixnoun oi nusers(%d) deiktes\n", nusers);
    printf("h metablhth users exei dieu8insi %p kai deixnei se %p\n", (void *)&res->users, (void *)res->users);
Έξοδος:

Prin to malloc o res deixnei se (nil)
zhthsa apo thn malloc 32 bytes gia thn res kai tora deixnei sthn 0x5639f85b0360
as doyme pou deixnoun oi nusers(2) deiktes
h metablhth users exei dieu8insi 0x5639f85b0368 kai deixnei se 0x7f526ccffb88
Πριν να τρέξεις το αρχικό malloc ο res έχει "μηδενική" τιμή. Ώς εδώ καλά. Εσύ μετά πας και ζητάς από την malloc συνεχόμενη μνήμη όσο για την δομή σου και όσο για δύο (nusers) δείκτες σε pam_usb_user. Υποθέτω ότι το έκανες γιατί διάβασες κάπου για το struct hack.

 

Την μεταβλητή users όμως την δήλωσες σαν διπλό δείκτη και όχι σαν μονό που είναι στο struct hack. Διάβασε τώρα τι αποτέλεσμα παίρνουμε στην έξοδο. Η δομή σου ξεκινά στην διεύθυνση τάδεb0360 και τελειώνει + 32 bytes μετά. Η μεταβλητή users έχει διεύθυνση b0368 δηλαδή 8 bytes μετά την αρχή της δομής όπως είναι και το αναμενόμενο.

 

Που δείχνει όμως ? Δείχνει στην διεύθυνση 0x7f526ccfb88 δηλαδή σε όποιο σκουπίδι υπήρχε στην διεύθυνση b0368.

 

Δοκίμασε μετά το res = malloc να βάλεις "res->users = &res->users + 1;" και δες αν βαράει segmentation fault μετά. Λογικά δεν θα πρέπει να βαράει αλλά φυσικά δεν είναι η διόρθωση αυτή. Διόρθωση είναι να γραφεί σωστά ο κώδικας.

Επεξ/σία από imitheos
  • Like 1
Δημοσ. (επεξεργασμένο)


	for(i = 0; i < nusers; i++){
		if((user = config_setting_get_elem(users, i)) == NULL) continue;
		if((devices = config_setting_get_member(user, "devices")) == NULL){
			msg = "no devices for user"; goto error;
		}
		int ndevices = config_setting_length(devices);
		res->users[i] = malloc(sizeof(struct usb_user));
		res->users[i].devices = malloc(ndevices * sizeof(struct usb_device));
		res->users[i].ndevices = ndevices;
		if(config_setting_lookup_string(user, "login", &tmp) == CONFIG_FALSE){
			msg = "no login name specified"; goto error;
		}
		res->users[i].login = strdup(tmp);

error: incompatible types when assigning to type ‘struct usb_user’ from type ‘void *’

   res->users = malloc(sizeof(struct usb_user));

                 ^

 

Τι κάνω λάθος τώρα?

Επεξ/σία από Anubis13
Δημοσ.

 

struct usb_conf {
    size_t nusers;
    struct usb_user *users;
};

int read_config(struct usb_conf **conf, const char *config_file) {
	for(i = 0; i < nusers; i++){
		res->users[i] = malloc(sizeof(struct usb_user));
	}
}
 

error: incompatible types when assigning to type ‘struct usb_user’ from type ‘void *’
   res->users[i] = malloc(sizeof(struct usb_user));
                 ^
Τι κάνω λάθος τώρα?

 

Σου λέει ότι η malloc επιστρέφει ένα δείκτη σε void και εσύ προσπαθείς να το θέσεις ως τιμή όχι σε ένα δείκτη αλλά σε μια μεταβλητή τύπου struct usb_user.

 

Εφόσον στη δομή δήλωσες "struct usb_user *users", τότε μόνο η μεταβλητή users είναι δείκτης. Γράφοντας "res->users[τάδε]" (αν σε βολεύει να το καταλάβεις καλύτερα σκέψου ότι αντί για users[τάδε] έγραψες *(users + τάδε) που φαίνεται καλύτερα ότι κάνεις dereference την μεταβλητή users) κάνεις dereference και παίρνεις απλές μεταβλητές τύπου struct usb_user οπότε δεν μπορείς να κάνεις malloc εκεί.

 

Με αυτή την σύνταξη της δομής, θα κάνεις μία φορά malloc στην users δίνοντας το ολικό μέγεθος που θέλεις πχ res->users = malloc(τάδε νούμερο * sizeof(struct usb_user)) και μετά προσπελαύνεις κανονικά με users[τάδε] όπως το έχεις.

 

Από ποιο βιβλίο μαθαίνατε C ? Πάρε ένα καλό βιβλίο (πχ αυτό του King αν και είναι ακριβό είναι καλό) και ξαναδιάβασε τα κεφάλαια πινάκων - δεικτών.

  • Like 1

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...