Non riesco a capire!

di il
56 risposte

56 Risposte - Pagina 3

  • Re: Non riesco a capire!

    Non lo traduci in Asm ... sono chiamate a funzioni complesse di alcune librerie ...

    Un puntatore è valido quando con questo ci accedi a memoria visibile al processo. Ma non ti basta per sapere se stai leggendo/scrivendo da dove credi tu o da altre parti. Non c'è modo diretto avendo solo il puntatore. Perché non te ne convinci? Mah ...

    E comunque continui a non rispondermi sulla domanda che ti ho fatto ... se passi ESI come puntatore ad una procedura Asm, come sai se ESI è valido o no?
  • Re: Non riesco a capire!

    Sono sempre contento di vedere qualcuno programmare in assembly, ma in questo caso mi sembra un overkill. C'è tutta una famiglia di funzioni WinApi per controllare se un puntatore è valido:
    IsBadCodePtr
    IsBadHugeReadPtr
    IsBadHugeWritePtr
    IsBadReadPtr
    IsBadStringPtr
    IsBadWritePtr
    Per esempio, questo loop usa IsBadReadPtr; se eax==0, il puntatore è valido, altrimenti no:
      mov esi, 1
      xor edi, edi
      .Repeat
    	shl esi, 1
    	invoke IsBadReadPtr, esi, 100
    	inc edi
    	xchg eax, ecx
    	Print Hex$(esi), Str$("\tretval: %i\n", ecx)
      .Until edi>=31
    Risultato:
    00000002        retval: 1
    00000004        retval: 1
    00000008        retval: 1
    00000010        retval: 1
    00000020        retval: 1
    00000040        retval: 1
    00000080        retval: 1
    00000100        retval: 1
    00000200        retval: 1
    00000400        retval: 1
    00000800        retval: 1
    00001000        retval: 1
    00002000        retval: 1
    00004000        retval: 1
    00008000        retval: 1
    00010000        retval: 0
    00020000        retval: 0
    00040000        retval: 0
    00080000        retval: 1
    00100000        retval: 1
    00200000        retval: 1
    00400000        retval: 0
    00800000        retval: 0
    01000000        retval: 1
    02000000        retval: 0
    04000000        retval: 1
    08000000        retval: 1
    10000000        retval: 1
    20000000        retval: 1
    40000000        retval: 1
    80000000        retval: 1
  • Re: Non riesco a capire!

    jj2007 ha scritto:


    Sono sempre contento di vedere qualcuno programmare in assembly, ma in questo caso mi sembra un overkill. C'è tutta una famiglia di funzioni WinApi per controllare se un puntatore è valido:
    IsBadCodePtr
    IsBadHugeReadPtr
    IsBadHugeWritePtr
    IsBadReadPtr
    IsBadStringPtr
    IsBadWritePtr
    Quelle APi sono vecchie e NON garantiscono quello che dicono di fare. Non possono essere usate.
  • Re: Non riesco a capire!

    . Ecco cosa fa IsBadReadPtr:
    IsBadReadPtr        Ú$ Ú6A 0C                     push 0C                                 ; BOOL kernel32.IsBadReadPtr(Addr,Size)
    753CBADD            ³. ³68 40BB3C75               push 753CBB40
    753CBAE2            ³. ³E8 D95AFDFF               call 753A15C0                           ; installa exception handler, vedi sotto
    753CBAE7            ³. ³8B35 88034775             mov esi, [75470388]
    753CBAED            ³. ³8B45 0C                   mov eax, [ebp+0C]
    753CBAF0            ³. ³85C0                      test eax, eax
    753CBAF2            ³.³74 3F                     jz short 753CBB33
    753CBAF4            ³. ³8B4D 08                   mov ecx, [ebp+8]
    753CBAF7            ³. ³85C9                      test ecx, ecx
    753CBAF9            ³.³0F84 1D78FFFF             jz 753C331C
    753CBAFF            ³. ³8D4401 FF                 lea eax, [eax+ecx-1]
    753CBB03            ³. ³8945 0C                   mov [ebp+0C], eax
    753CBB06            ³. ³3BC1                      cmp eax, ecx
    753CBB08            ³.³0F82 0E78FFFF             jb 753C331C
    753CBB0E            ³. ³8365 FC 00                and dword ptr [ebp-4], 00000000
    753CBB12            ³. ³8A01                      mov al, [ecx]                           ; un piccolo test
    753CBB14            ³. ³8D56 FF                   lea edx, [esi-1]
    753CBB17            ³. ³F7D2                      not edx
    753CBB19            ³. ³8BC2                      mov eax, edx
    Exception handler:
    753A15C0            Ú$  68 CB494475               push 754449CB
    753A15C5            ³.  64:FF35 00000000          push dword ptr fs:[0]
    753A15CC            ³.  8B4424 10                 mov eax, [esp+10]
    753A15D0            ³.  896C24 10                 mov [esp+10], ebp
    753A15D4            ³.  8D6C24 10                 lea ebp, [esp+10]
    753A15D8            ³.  2BE0                      sub esp, eax
    753A15DA            ³.  53                        push ebx
    753A15DB            ³.  56                        push esi
    753A15DC            ³.  57                        push edi
    753A15DD            ³.  A1 AC034775               mov eax, [754703AC]
    753A15E2            ³.  3145 FC                   xor [ebp-4], eax
    753A15E5            ³.  33C5                      xor eax, ebp
    753A15E7            ³.  50                        push eax
    753A15E8            ³.  8965 E8                   mov [ebp-18], esp
    753A15EB            ³.  FF75 F8                   push dword ptr [ebp-8]
    753A15EE            ³.  8B45 FC                   mov eax, [ebp-4]
    753A15F1            ³.  C745 FC FEFFFFFF          mov dword ptr [ebp-4], -2
    753A15F8            ³.  8945 F8                   mov [ebp-8], eax
    753A15FB            ³.  8D45 F0                   lea eax, [ebp-10]
    753A15FE            ³.  64:A3 00000000            mov fs:[0], eax	; <<<<<<<<<<< eccolo! <<<<<<<<<<<<<<<<
    753A1604            À.  C3                        retn
    E quando arrivi a "un piccolo test", quel mov al, [ecx] ti porta subito qui:
    KiUserExceptionDisp Ú$  FC                        cld                                     ; ntdll.KiUserExceptionDispatcher(pExceptionRecord,pContext)
    Ovviamente, un programmatore più esperto di quelli di Micros**t potrebbe scrivere un SEH migliore
  • Re: Non riesco a capire!

    Grazie ancora per l'articolo era pieno di cose interessantissime
    e che non conoscevo
    ma purtroppo non ho trovato idee per risolvere.

    oregon ha scritto:


    se passi ESI come puntatore ad una procedura Asm, come sai se ESI è valido o no?
    che io sappia su un mono thred
    premesso che si appesantisce parecchio il programma.
    si prova a fare una lettura dell'area indicizzata dal puntatore
    e se restituisce una eccezione la si gestisce
    o
    si fa la mappatura delle aree della memoria e poi si verifica il puntatore con i range della mappatura.
    il problema della mappatura e che, oltre l'appesantimento spropositato del programma, con le allocazioni dinamiche diventa quasi impossibile da realizzare,almeno per me.
  • Re: Non riesco a capire!

    É proprio impossibile perché non puoi fare operazioni pesanti ad ogni accesso
  • Re: Non riesco a capire!

    Per questo speravo nel controllo del puntatore.
    quando torno a casa provo le funzioni suggerite da jj2007
  • Re: Non riesco a capire!

    @smalldragon
    NON è la prima volta che parliamo di quelle funzioni in una tua discussione ...
    Quelle funzioni sono obsolete e NON affidabili. Leggi la documentazione!

    Important This function is obsolete and should not be used. Despite its name, it does not guarantee that the pointer is valid or that the memory pointed to is safe to use. For more information, see Remarks on this page.


    @jj2007
    Non so se ms ha sempre ragione ma è poco sensato non fidarsi della documentazione ... e di tante altri ... che già dal 2006 scrivevano

    IsBadXxxPtr should really be called CrashProgramRandomly
    https://devblogs.microsoft.com/oldnewthing/20060927-07/?p=29563

    o anche

    The reason why they're bad is listed in the links you've provided. Due to how the Windows OS is designed, the way the functions were coded, and the fact that determining if memory is valid is hard makes these functions inherently unstable to use.

    There is no good way to determine if a pointer is using a valid part of memory. Freed memory doesn't necessarily get zeroed out or reset. If there is a bad pointer floating around it could point to data that in a valid portion of memory. Just because you can dereference the pointer doesn't mean that the data itself is valid.

    Those two functions use page guard exceptions to determine if the memory is valid. The design of these exceptions is not supposed to be used as a "valid memory address" check. As described by the first link:

    The IsBadXxxPtr function will catch the exception and return “not a valid pointer”. But guard page exceptions are raised only once. You just blew your one chance. When the code that is managing the guard page accesses the memory for what it thinks is the first time (but is really the second), it won’t get the guard page exception but will instead get a normal access violation

    Basically even by using these functions you can cause access violations because of the overall design of the Windows OS, and how these functions were coded. Even if you're not using page guards there's no guarantee that other shared libraries that you're loading aren't using page guards.

    Trying to catch exceptions from the OS that are trying to tell you that things have gone terribly wrong is a bad idea. There is a programming error somewhere and the best option is to crash. Trying to return to a good state when things go that wrong can cause undetermined behavior.

    The bottom line is you can't fix what went wrong with memory. Continuing execution will only burn you later.


    ma se tu sei tanto sicuro accomodati ...
  • Re: Non riesco a capire!

    oregon ha scritto:


    Non so se ms ha sempre ragione ma è poco sensato non fidarsi della documentazione ... e di tante altri ... che già dal 2006 scrivevano

    IsBadXxxPtr should really be called CrashProgramRandomly
    https://devblogs.microsoft.com/oldnewthing/20060927-07/?p=29563
    ....
    Those two functions use page guard exceptions to determine if the memory is valid. The design of these exceptions is not supposed to be used as a "valid memory address" check.
    Raymond Chen ha spesso ragione ma non sempre: non usano page guard exceptions, usano quelle vere, cioè installano davvero un SEH temporaneo.

    oregon ha scritto:


    Se la memoria a cui punta il puntatore non è allocata al processo (ad esempio il puntatore punta a NULL) allora puoi intercettare l'eccezione (anche semplicemente da C++ senza ASM) e operare di conseguenza. Ma se il puntatore punta casualmente a memoria del processo (ad altra variabile ad esempio), questo codice (e nessun altro codice) non ti potrà aiutare.
    Giusto. Ma se sei arrivato a questo punto, puoi anche sostituire il codice un po' caotico dell'OP con un semplice IsBadReadPtr perché fanno la stessa cosa: Provano se scatta un eccezione se leggi dall'indirizzo XXXX.

    Del resto, non capisco la necessità di questa funzione. Mi è capitato mille volte di leggere da un indirizzo invalido mentre debuggavo (si dice così?) un nuovo programma. Non mi è mai capitato di chiedermi se un indirizzo non-zero fosse valido. Sono io da programmatore a ottenere un indirizzo via Alloc*, sono io a metterlo nella variabile pBuffer, e sono io ad azzerrarlo con Free*. Se non so se l'indirizzo è valido, qualcosa è andato molto storto.
  • Re: Non riesco a capire!

    @jj2007
    ho provato la funzione ma mi da lo stesso risultato di quello che avevo scritto io, problema non risolto.
    il problema è che io so già che il puntatore e valido ma non so fino a quando lo è.
    esempio
    mi alloco una stringa di 5 byte
    passo il puntatore ad una routine che scrive in questa variabile ma che può gestire altre variabili più grandi.
    quando vado a rilasciare la variabile,quella di 5 byte, giustamente mi da un errore di heap corruption.
    adesso io vorrei che il programma mi restituisse l'errore non in fase di rilascio della memoria
    ma in fase di scrittura cosi da poter intervenire subito e correggere l'errore.
    senza dovermi portare sempre a presso la dimensione della variabile o dovermela calcolare ogni volta.
    hai qualche consiglio da darmi?
  • Re: Non riesco a capire!

    smalldragon ha scritto:


    @jj2007
    ho provato la funzione ma mi da lo stesso risultato di quello che avevo scritto io, problema non risolto.
    il problema è che io so già che il puntatore e valido ma non so fino a quando lo è.
    esempio
    mi alloco una stringa di 5 byte
    passo il puntatore ad una routine che scrive in questa variabile ma che può gestire altre variabili più grandi.
    quando vado a rilasciare la variabile,quella di 5 byte, giustamente mi da un errore di heap corruption.
    adesso io vorrei che il programma mi restituisse l'errore non in fase di rilascio della memoria
    ma in fase di scrittura cosi da poter intervenire subito e correggere l'errore.
    senza dovermi portare sempre a presso la dimensione della variabile o dovermela calcolare ogni volta.
    hai qualche consiglio da darmi?
    A parte il fatto che e' gia' stato detto, ti sono gia' stati forniti i link di libri che spiegano come si fa, ma laciamo perdere.

    Ti crei un ALLOCATORE CUSTOM

    Quando allochi un blocco di memoria, aggiungi in testa ed in coda le seuenti informazioni (quidni devi allocare PIU' SPAZIO di quello richiesto):
    1) testa: la dimensione del blocco di memoria allocato e un MAGIC_NUMBER_HEAD
    2) coda: una funzione della dimensione e del MAGIC_NUMBER_HEAD (MAGIC_NUMBER_TAIL)
    3) ritorni il puntatore NON all'area allocata (che contiene le informazioni 1) e 2) ) MA all'area disponibile per l'applicazione
    4) OGNI VOLTA che accedi al puntatore, PRIMA di usarlo ricostruisci il puntatore originale e valuti la presenza di MAGIC_NUMBER_HEAD e MAGIC_NUMBER_TAIL
    5) se e' tutto a posto, il puntatore e' valido, ALTRIMENTI il puntatore e' corrotto
    6) quando rilasci il blocco di memoria, azzerri il blocco e magari al posto dei MAGIC_NUMBER_XXX ci metti un nuovo MAGIC_NUMBER_DELETED, in quest modo, se tenti di usare il puntatore e trovi MAGIC_NUMBER_DELETED sai che il blocco e' stato cancellato, se trovi valori strani, stai usanso un puntatore che non sta' puntando a niente di precedentemente allocato

    Questo NON RISOLVE TOTALMENTE il prolema dei puntatori non validi o della possibilita' di scriver AL DI FUORI dell'area allocata perche' potrebbe sempre capitare di usare un puntatore che sta' puntando ad un'area di memoria rilasciata e poi riallocata da un'altra parte del programma per tutti altri motivi, OPPURE che sei andato a scrivere AL DI FUORI dell'area di memoria ESATTAMENTE i byte corrispondeti al MAGIC_NUMBER_TAIL

    MA, se i valori sono stati selezionati con cura, dovrebbe essere abbastanza difficile trovarsi nei casi sudetti.

    Questo funziona SOLO con lo heap, NON con le variabili globali o quelle allocate sullo stack!
  • Re: Non riesco a capire!

    @smalldragon, quando non riesci a risolvere un problema, ti devi porre la domanda: ma e' ESATTAMENTE questo il problema?

    La gestione della memoria in C/C++ E' COMPLICATA e ci sono n-mila strategie per tentare di semplificarla, ma TUTTE si scontrano con il problema che il puntatore e' gestito dal processo e NON dalla CPU: NON ESISTONO dei "bound" che la CPU puo' usare per sapere se il puntatore e' valido oppure no.

    ESISTONO dei bound a livello HARDWARE,che sono associati ai blocchi di memoria gestiti dal sistema operativo ( ma i blocchi sono di GROSSE dimensioni, dovrebbero essere multipli di 4 KB, forse, boh!) che servono per far si che un processo NON POSSA ANDARE a leggere/scrivere nell'area di memoria di un'altro processo (se non sotto il controllo del SO che puo' leggere OVUNQUE), o a scrivere nell'area di memoria del codice

    Ora, ti porpongo un'altro problema,DECISAMENTE piu' interessante:

    OVVIAMENTE il gestore della memoria, potendo essere usato da PIU' thread contemporaeamente, DEVE NECESSARIAMENTE serializzare le richieste.
    Questo, se ci sono molti thread e in ogni thread molte allocazioni di piccoli oggetti, RALLENTA SIGNIFICATIVAMENTE l'applicazione.

    TROVA un modo per RIDURRE SIGNIFICATIVAMENTE questo overhead, in particolare, devi trovare il modo di RIMUOVERE (per quanto possibile) il MUTEX che serializza le allocazioni/deallocazioni

  • Re: Non riesco a capire!

    @smalldragon, la proposta di migliorabile è migliorabile, perché in fondo è molto, molto semplice:

    - allochi una stringa, e metti il puntatore in pString
    - passi l'indirizzo di pString ad una routine
    - la tua routine fa quello che vuole con la stringa, MA:
    - se cambia la dimensione della stringa, mette il nuovo puntatore in pString
    - se cancella la stringa, mette ZERO in pString

    Al ritorno dalla routine, pString è valido, punto. Se ci trovi uno zero, sai che la stringa è stata cancellata. Tutto qua, nessun altro test necessario.
  • Re: Non riesco a capire!

    @jj2007
    NON FUNZIONA!

    C'e' un ASSIOMA che si DEVE SEMPRE tenere presente quando si programma in C/C++ (almeno per i principianti)

    DEALLOCA CHI ALLOCA

    perche' SOLO chi ha allocato SA quando e come deallocare.

    Ci sono strategie MOOLTO piu' sofisticate per far si che a deallocare possa essere qualcun altro, ma per fare questo bisogna coinvolgere il concetto di "garbage collector" in una delle tante varianti. Ad esempio il "reference counting", alla base degli "smart pointers".

    La routine NON HA IDEA di che tipo di puntatore di tratta. Potrebbe essere un puntatore nello stack, un blocco di memoria in una struttura dati piu' grossa, ecc. QUINDI NON PUO' DEALLOCARLA, la puo' solo usare.

    La tua idea funziona SE alla funzioni NON PASSI il puntatore ma UNA STRUTTURA DATI RESPONSABILE DEL PUNTATORE.
    In questo caso,

    1) LA STRUTTURA DATI DEVE ESSERE l"UNICA RESPONSABILE della gestione del puntatore
    2) NON SI USA MAI DIRETTAMENTE il puntatore, ma VA RICHIESTO OGNI VOLTA alla struttura dati, perche' non c'e' modo di sapere se il puntatore corrente e' lo stesso usato l'ultima volta.
    3) il puntatore non va copiato o propagato in giro per il codice

    gli oggetti "std::string", "std::vector" ecc, delle STL funzionano esattamente in questo modo.

    Pessimo tentativo
  • Re: Non riesco a capire!

    Se devi fare stringhe ASCII è semplice.
    Inizializzazione heap: tutti i byte a FF.
    Allocazione: n+1 byte, i primi n a 00 l'ultimo a FF.
    Deallocazione: n+1 byte a FF.
    Scrittura: prima di farla controllo che il byte sia diverso da FF, altrimenti è zona vietata.

    Ma comunque ha ragione Oregon: tutto ciò è inutile
Devi accedere o registrarti per scrivere nel forum
56 risposte