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

malloc & free


sonyxp

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

  • Απαντ. 63
  • Δημ.
  • Τελ. απάντηση

Συχνή συμμετοχή στο θέμα

Συχνή συμμετοχή στο θέμα

@imitheos & @albNik:

 

Όλα περί NULL αναλύονται διεξοδικά εδώ: http://c-faq.com/null/index.html

 

Κάνα-δυο quoestions που διάβασα λένε ότι κι εσείς, αλλά έχω την εντύπωση πως είχα διαβάσει για κάποιες εξαιρέσεις κάπου εκεί μέσα παλαιότερα (πολύ παλαιότερα)... δεν έχω όμως τώρα το κουράγιο να ξανα διαβάσω όλα τα questions (πάντως consider και την περίπτωση της memset() η οποία αν εφαρμοστεί με 2ο όρισμα 0 σε ένα struct που περιέχει δείκτες, νομίζω δεν εγγυάται πως το αποτέλεσμα θα είναι να τεθούν σε NULL οι δείκτες αυτοί).

 

Όπως και να έχει, ακριβώς επειδή δεν είμαι σίγουρος, προτιμώ να το play safe και να χρησιμοποιώ NULL αντί για 0 (αν και συχνά χρησιμοποιώ και τα if ( p )  & if ( !p ) θεωρώντας πως ο compiler ξέρει πάντα να τα κάνει σωστά convert αν και όταν χρειάζεται, σαν idioms δηλαδή).

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Migf, καταρχάς ευχαριστώ που προσπαθείς να με βοηθήσεις

 

Νομίζω δεν με καταλάβατε, αυτό που είπα είναι ότι και μετά την απελευθέρωση μνήμης οι τιμές παραμένουν στις ΘΜ, όχι ότι έχουμε πρόσβαση!

Δοκίμασα τον κώδικα σου  (απλά πρόσθεσα random τιμές και όχι 0νικα με την calloc)

 


image.png
 

image.png

 

 

 

 

Αυτό που θέλω είναι να μην υπάρχουν καν οι τιμές γιατί με δόλιους τρόπους μπορεί κάποιος να αποκτήσει πρόσβαση σε αυτές τις διευθύνσεις παρολο που είναι free

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Migf, καταρχάς ευχαριστώ που προσπαθείς να με βοηθήσεις

Για αυτό είναι τα φόρουμ :)

 

Νομίζω δεν με καταλάβατε, αυτό που είπα είναι ότι και μετά την απελευθέρωση μνήμης οι τιμές παραμένουν στις ΘΜ, όχι ότι έχουμε πρόσβαση!

Στον κώδικα που δίνεις όμως κάνεις free() και μετά πας και προσπαθείς να κάνεις print αυτά που έχεις κάνει free()(σαν να είχες δηλαδή πρόσβαση).

 

Αυτό που θέλω είναι να μην υπάρχουν καν οι τιμές γιατί με δόλιους τρόπους μπορεί κάποιος να αποκτήσει πρόσβαση σε αυτές τις διευθύνσεις παρολο που είναι free

Πριν κάνεις free() κάνε memset( array, 0, M * sizeof(int) ); ώστε να μηδενίσεις εκείνη τη μνήμη πριν την κάνεις free().

 

EDIT:

 

Btw, στον κώδικά μου κάνω print πριν κάνω free() το array. Εσύ κάνεις και πριν και μετά (το μετά βγάζει undefined behaviour... EDIT2 (βλέπε: http://www.insomnia.gr/topic/484331-malloc-free/?do=findComment&comment=52324630): σε μένα αν κάνεις print μετά το free θα σου σκάσει seg-fault, γιατί μετά το free θέτω το array σε NULL ).

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

 

 

Αυτό που θέλω είναι να μην υπάρχουν καν οι τιμές γιατί με δόλιους τρόπους μπορεί κάποιος να αποκτήσει πρόσβαση σε αυτές τις διευθύνσεις παρολο που είναι free

 

Μ αυτή τη λογική μπορεί να έχει προσβαση κάποιος και πριν γίνουν free.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

 

Τώρα, μετά το free εσύ έχεις φυλάξει τη διεύθυνσή του και απλά το προσπελαύνεις ξανά.

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

 

 

 

Αυτο εννοω φιλε, οχι πως εχω κατι να κρυψω αλλα θελω να γινει σωστα η δουλεια

 

 

TIP: Αν για παράδειγμα έγραφες μια εφαρμογή που είχε πολύ ευαίσθητα δεδομένα στη μνήμη, θα μπορούσες πριν το free να τα "καθαρίζεις" (γράφεις κάτι άλλο σε αυτά) χειροκίνητα ώστε να αποφύγεις τυχών κακόβουλους "αναγνώστες'.

 

EDIT: Με πρόλαβαν οι παραπάνω. Με καλύπτουν απόλυτα.

 

Αυτό προσπαθώ να αποφύγω (περίμενα αυτό να γίνεται αυτόματα, δηλαδή να γραφόντουσαν "σκουπίδια"), αλλα μαλλον η μονη λυση ειναι ενα περασμα με αλλες τιμες για να σβησουν τις παλιες.

 

 

Θανκς

 

 

 

 

 

Μ αυτή τη λογική μπορεί να έχει προσβαση κάποιος και πριν γίνουν free.

 

οσο ειναι δεσμευμενο το κομματι θα πρεπει να εισαι ο ατυχος του ατυχου ο ατυχε για να σε κανει τιποτα κανεις...

 

μετα που αποδεσμευονται ειναι το προβλημα :P, τα αποδεσμευεις με μια βαλιτσα με χρυσαφι σαν να λεμε...

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

 

...

οσο ειναι δεσμευμενο το κομματι θα πρεπει να εισαι ο ατυχος του ατυχου ο ατυχε για να σε κανει τιποτα κανεις...

 

μετα που αποδεσμευονται ειναι το προβλημα :P, τα αποδεσμευεις με μια βαλιτσα με χρυσαφι σαν να λεμε...

 

Γενικώς δεν ισχύει αυτό. Από την στιγμή που ξέρει το address space του προγράμματός σου δεν του κάνει καμία διαφορά το αν έχεις δεσμευμένη ή αδέσμευτη μνήμη εκεί μέσα. Μπορεί να πειράξει ότι θέλει.

 

Δηλαδή ισχύει αυτό που σου είπε ο albNik.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Για αυτό είναι τα φόρουμ :)

 

Στον κώδικα που δίνεις όμως κάνεις free() και μετά πας και προσπαθείς να κάνεις print αυτά που έχεις κάνει free()(σαν να είχες δηλαδή πρόσβαση).

 

Πριν κάνεις free() κάνε memset( array, 0, M * sizeof(int) ); ώστε να μηδενίσεις εκείνη τη μνήμη πριν την κάνεις free().

 

EDIT:

 

Btw, στον κώδικά μου κάνω print πριν κάνω free() το array. Εσύ κάνεις και πριν και μετά (το μετά βγάζει undefined behaviour... σε μένα αν κάνεις print μετά το free θα σου σκάσει seg-fault, γιατί μετά το free θέτω το array σε NULL).

Μάθαμε και σήμερα κάτι,

 

1. επικάλυψη με άλλες τιμές (px memset <- easy way)

2. κανεις free

3. βαζεις και ενα *myVar = NULL για να εχεις το κεφάλι ήσυχο

 

Ναστε καλα ολοι, αυτο ηθελα

 

 

Γενικώς δεν ισχύει αυτό. Από την στιγμή που ξέρει σε το address space του προγράμματός σου δεν του κάνει καμία διαφορά το αν έχεις δεσμευμένη ή αδέσμευτη μνήμη εκεί μέσα.

 

Δηλαδή ισχύει αυτό που σου είπε ο albNik.

Και πως επιτρεχει το ΛΣ καποιος 3ος να εχει προσβαση (εστω και READ) στην δεσμευμενη μνημη απο καποιο προγραμμα?

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

...

 

Και πως επιτρεχει το ΛΣ καποιος 3ος να εχει προσβαση (εστω και READ) στην δεσμευμενη μνημη απο καποιο προγραμμα?

 

Υπάρχουν διάφοροι τρόποι, οι περισσότεροι εκ των οποίων (αν όχι όλοι) εκμεταλλεύονται τρύπες (bugs) του λειτουργικού.

 

...

 

EDIT:

 

Btw, στον κώδικά μου κάνω print πριν κάνω free() το array. Εσύ κάνεις και πριν και μετά (το μετά βγάζει undefined behaviour... σε μένα αν κάνεις print μετά το free θα σου σκάσει seg-fault, γιατί μετά το free θέτω το array σε NULL).

 

Λάθος το γράφω, δεν θα σου σκάσει seg-fault στο δικό μου, απλώς δεν θα τυπώσει τίποτα γιατί έχω συμπεριλάβει sanity check μέσα στην print(), ώστε αν το array περαστεί ως NULL η συνάρτηση να επιστρέψει χωρίς να τυπώσει τίποτα.

 

Σόρυ για το μπέρδεμα και καλή συνέχεια.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

 

Και πως επιτρεχει το ΛΣ καποιος 3ος να εχει προσβαση (εστω και READ) στην δεσμευμενη μνημη απο καποιο προγραμμα?

 

Στα windows γινεται νομιμα αυτό  

http://msdn.microsoft.com/en-us/library/windows/desktop/ms680553(v=vs.85).aspx

 

και write μαλλον  :-D

http://msdn.microsoft.com/en-us/library/windows/desktop/ms681674(v=vs.85).aspx

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

 

Ναι, αλλά από μια γρήγορη ματιά που έριξα στα links, δεν έχουν πρόσβαση αυτές οι ρουτίνες σε inaccessible ranges του address space του προγράμματος.

 

Υποθέτω ο φίλος μιλούσε για απεριόριστη πρόσβαση στο address-space, που κατά κανόνα προϋποθέτει να μπεις από kernel mode.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Νομίζω δεν με καταλάβατε, αυτό που είπα είναι ότι και μετά την απελευθέρωση μνήμης οι τιμές παραμένουν στις ΘΜ, όχι ότι έχουμε πρόσβαση!

 

Αυτό που θέλω είναι να μην υπάρχουν καν οι τιμές γιατί με δόλιους τρόπους μπορεί κάποιος να αποκτήσει πρόσβαση σε αυτές τις διευθύνσεις παρολο που είναι free

 

Αν θέλεις να σβήνονται οι τιμές από τη μνήμη απλά κάνε memset ή κάτι αντίστοιχο πριν το free όπως είπαν τα παιδιά. Στην απάντησή μου είπα πως καλά θα ήταν να σκεφτείς για ποιό λόγο ακριβώς περιμένεις να δεις τη συμπεριφορά που περιμένεις, το έκανες;

 

Επίσης, αν μπορεί να αποκτήσει κάποιος πρόσβαση στο address space της εφαρμογής σου (δηλαδή αν το σύστημα permissions του ΛΣ το επιτρέψει) δεν έχει ιδιαίτερο νόημα να προσπαθείς να αντισταθείς. Εσύ θα κάνεις compile το πρόγραμμα μία φορά και μετά αυτός ο κάποιος έχει άπειρο χρόνο και διαθέσιμες προσπάθειες για να ξεπεράσει τα οποιαδήποτε μέτρα μπορεί να έχεις πάρει.

 

Αυτο εννοω φιλε, οχι πως εχω κατι να κρυψω αλλα θελω να γινει σωστα η δουλεια

 

"Σωστά;" Δηλαδή αν τις αφήσεις στην ησυχία τους τι είναι αυτό που δεν γίνεται σωστά;

 

Και πως επιτρεχει το ΛΣ καποιος 3ος να εχει προσβαση (εστω και READ) στην δεσμευμενη μνημη απο καποιο προγραμμα?

 

Σκέψου λίγο τι μπορεί να κάνει ένας debugger στην εφαρμογή που κάνεις debug. Όχι ότι χρειάζεται να είσαι debugger βέβαια.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δυστυχώς αν θέλει κάποιος να είναι 100% σίγουρος για την διαγραφή των περιεχομένων της μνήμης που δέσμευσε και ύστερα απελευθέρωσε (σε χαμηλό επίπεδο -swap file), θα πρέπει να βασιστεί σε ασφαλείς συναρτήσεις διαχείρισης μνήμης που ενδεχομένως προσφέρει το λειτουργικό του σύστημα (βλ. SecureZeroMemory, CryptProtectMemory κλπ σε Windows API) ώστε να εξασφαλίσει πως τα δεδομένα του είτε θα διαγραφούν οριστικά από το swap-file του λειτουργικού ή τουλάχιστον θα εγγραφούν σε κρυπτογραφημένη μορφή δυσχεραίνοντας το έργο διαφόρων forensics tools, πέραν αυτών των ειδικών συναρτήσεων διαχείρισης μνήμης ο έλεγχος που μπορεί να έχει ο προγραμματιστής σε επίπεδο swap-file (και τι τελικά θα κάνει ο memory-manager του Λ.Σ.) είναι μηδενικός.

 

 

swap-file .. μέγας ρουφιάνος :P

 

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

πάντως consider και την περίπτωση της memset() η οποία αν εφαρμοστεί με 2ο όρισμα 0 σε ένα struct που περιέχει δείκτες, νομίζω δεν εγγυάται πως το αποτέλεσμα θα είναι να τεθούν σε NULL οι δείκτες αυτοί.

Φυσικά και δεν το εγγυάται (μάλιστα όχι δεν είναι εγγυημένο να παίξει αλλά δεν θα πρέπει να παίξει ποτέ) γιατί η memset θέτει την τιμή 0 (δηλαδή όλα τα bits να είναι μηδέν) οπότε δεν έχεις null pointer. Για την ακρίβεια, το πρόβλημα της memset είναι ότι ως συνάρτηση που δέχεται από τον χρήστη την τιμή που θα θέσει χρησιμοποιεί μεταβλητή για να το κάνει. Μόνο η σταθερά (Integer Constant Expression) 0 είναι συμβολισμός για τον NULL δείκτη.

 

int k = 0;
int *p = (void *)k;
Με άλλα λόγια, ο παραπάνω κώδικας δεν δημιουργεί ένα NULL δείκτη αλλά ένα δείκτη που δείχνει στη διεύθυνση μνήμης 0. Αυτή η συμπεριφορά (με διαφορετικό τρόπο φυσικά) χρησιμοποιήθηκε πριν κάποια χρόνια σε ένα exploit στο linux για να γίνει mmap η σελίδα 0.

 

Νομίζω δεν με καταλάβατε, αυτό που είπα είναι ότι και μετά την απελευθέρωση μνήμης οι τιμές παραμένουν στις ΘΜ, όχι ότι έχουμε πρόσβαση!

 

Αυτό που θέλω είναι να μην υπάρχουν καν οι τιμές γιατί με δόλιους τρόπους μπορεί κάποιος να αποκτήσει πρόσβαση σε αυτές τις διευθύνσεις παρολο που είναι free

Οι C compilers (και οι standard libs) προσπαθούν πάντα να παράγουν γρήγορο κώδικα οπότε εννοείται πως στην πλειοψηφία τους δεν θα σβήσουν την μνήμη κατά την free. Ασχέτως όμως αν οι τιμές μένουν ή γράφονται άλλες, είναι λάθος να προσπελάσεις μνήμη που έχει γίνει free.

 

Πριν κάνεις free() κάνε memset( array, 0, M * sizeof(int) ); ώστε να μηδενίσεις εκείνη τη μνήμη πριν την κάνεις free().

Δυστυχώς αν θέλει κάποιος να είναι 100% σίγουρος για την διαγραφή των περιεχομένων της μνήμης που δέσμευσε και ύστερα απελευθέρωσε (σε χαμηλό επίπεδο -swap file), θα πρέπει να βασιστεί σε ασφαλείς συναρτήσεις διαχείρισης μνήμης που ενδεχομένως προσφέρει το λειτουργικό του σύστημα (βλ. SecureZeroMemory, CryptProtectMemory κλπ σε Windows API)

Η memset που πρότεινε ο migf1 και ο defacer είναι μια λύση ώστε να μην μείνουν οι τιμές. Το θέμα όμως είναι ότι θα τρέξει η memset ? Τουλάχιστον ο GCC βλέπει ότι μετά την memset γίνεται free και έτσι σε όλα τα optimizing levels την αφαιρεί εντελώς. Για να είσαι σίγουρος ότι θα τρέξει θα πρέπει ή να καταφύγεις σε κόλπα με volatile και λοιπά που έχουν τα δικά τους προβλήματα ή να χρησιμοποιήσεις μια συνάρτηση που έχει φτιαχτεί για αυτή τη δουλειά όπως πρότεινε ο DirectX και να χάσεις portability.

 

Αν δηλαδή, όπως είπε και ο defacer, το λειτουργικό δεν έχει δικλείδες για να αποτρέπει την πρόσβαση τότε τον ήπιες.

  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Μάθαμε και σήμερα κάτι,

 

1. επικάλυψη με άλλες τιμές (px memset <- easy way)

2. κανεις free

3. βαζεις και ενα *myVar = NULL για να εχεις το κεφάλι ήσυχο

 

Ναστε καλα ολοι, αυτο ηθελα

...

 

Βασικά δεν είναι τόσο απλό εφόσον μιλάμε για security stuff.

 

Πέρα από την πολύ εύστοχη απάντηση του DirectX, η χρήση της memset() που σου υπέδειξα χτες ήταν για να καταλάβεις στα γρήγορα πως μπορείς να κάνεις αυτό που ρώτησες, χωρίς φυσικά να σου εξασφαλίζει πως τα data σου είναι ασφαλή πριν το free(),

 

Τώρα ειδικά για την memset(), και πιο συγκεκριμένα σε χρήσεις της όπως η παραπάνω, πέφτει πολύ συχνά θύμα της "dead code removal" βελτιστοποίησης των compilers. Μια πρώτη portable προσέγγιση είναι να δηλωθεί το buffer (array) ως volatile αλλά και πάλι επαφίεται στις ορέξεις του compiler το πόσο aggressive optimization κάνει σε συνδυασμό με το πως υλοποιεί εσωτερικά την memset()... και τα αδελφάκια της.

 

Οπότε, για security reasons κι εφόσον μας ενδιαφέρει το portability, μια συνηθισμένη λύση είναι να γράφουμε δικές μας εκδοχές των memXXX() συναρτήσεων, πάντα με volatile buffers ως ορίσματα.

 

Δες κι αυτό το link που βρήκα με ένα πρόχειρο google που τα εξηγεί καλύτερα από μένα και δίνει και κώδικα: http://etutorials.org/Programming/secure+programming/Chapter+13.+Other+Topics/13.2+Erasing+Data+from+Memory+Securely/

 

@imitheos: Μαζί γράφαμε.

Φυσικά και δεν το εγγυάται (μάλιστα όχι δεν είναι εγγυημένο να παίξει αλλά δεν θα πρέπει να παίξει ποτέ) γιατί η memset θέτει την τιμή 0 (δηλαδή όλα τα bits να είναι μηδέν) οπότε δεν έχεις null pointer. Για την ακρίβεια, το πρόβλημα της memset είναι ότι ως συνάρτηση που δέχεται από τον χρήστη την τιμή που θα θέσει χρησιμοποιεί μεταβλητή για να το κάνει. Μόνο η σταθερά (Integer Constant Expression) 0 είναι συμβολισμός για τον NULL δείκτη.

 

...

 

Το πρότυπο δεν το εγγυάται, πλην όμως παίζει κανονικά σε όλα τα 0-bits implementations, που είναι και η τυπική υλοποίηση στους δημοφιλείς compilers.

 

Π.χ. σε mingw-gcc (αλλά το ίδιο ισχύει και σε pelles-c και σε lcc που επίσης δοκίμασα ξανά επί τούτου... ομοίως και σε ubuntu gcc από ότι θυμάμαι, αν και βαρέθηκα να ανοίξω τώρα το laptop) ...

 

 

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct Foo {
    int *ptr;
};

int main( void )
{
    struct Foo foo;

#ifdef SAFE
    puts( "SAFE:" );
    foo.ptr = NULL;
#else
    puts( "UNSAFE:" );
    memset( &foo, 0, sizeof(struct Foo) );
#endif

    printf( "%sNULL\n", foo.ptr ? "not " : "\0" );

    exit(0);
}
 
/*
> gcc -D=SAFE -std=c89 -pedantic -Wall -Wextra _test0.c -o _test0.exe
> _test0
SAFE:
NULL

> gcc -std=c89 -pedantic -Wall -Wextra _test0.c -o _test0.exe
> _test0
UNSAFE:
NULL

>
 */

 

 

 

Προσωπικά έχω συναντήσει πάμπολλες φορές κώδικες με την παραπάνω πρακτική και να βασίζονται κατόπιν στο ότι η memset( , 0, ) θέτει τους δείκτες σε NULL, κάτι που μπορεί να (θα) δημιουργήσει προβλήματα αν μεταφερθεί σε compilers με non-0-bits implementation.

 

Το αν και ποιοι compilers είναι strictly conformant στα πρότυπα καθώς και το αν πρέπει ή όχι να είναι, είναι αμφιλεγόμενο ανάλογα το context ... είναι επίσης άλλη κουβέντα.

 

Οπότε εγώ ως defensive style όταν θέλω να είμαι portable έχω εμπεδώσει το να αποφεύγω να κάνω treat το NULL explicitly ως 0, για να μην χρειάζεται να θυμάμαι τέτοιες εξαρτήσεις και διαφορές μεταξύ αναθέσεων και συγκρίσεων (αν και όπως έγραψα και πριν, χρησιμοποιώ συχνά τα if (p) και if (!p) επειδή πάντα μου δουλεύουν... και παρόλο που δεν θυμόμουν τις λεπτομέρειες του γιατί το πιο προηγούμενο post σου μου το υπενθύμισε).

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

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

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

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

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

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

Σύνδεση

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

Συνδεθείτε τώρα

  • Δημιουργία νέου...