; gpfix.asm - pointer validation routines include gpfix.inc include kernel.inc include tdb.inc include newexe.inc sBegin GPFIX0 __GP label word ;gpbeg dw 0, 0, 0, 0 ; for use in handler public __GP sEnd GPFIX0 sBegin GPFIX1 gpend dw 0 sEnd GPFIX1 sBegin DATA ;this segment is page locked and will be accessible during a GP fault ;has the names of modules that are allowed to use our funky GP fault handling ; mechanism. Format: length byte, module name. The table is zero-terminated. gp_valid_modules label byte db 3, "GDI" db 4, "USER" db 6, "KERNEL" db 6, "PENWIN" db 7, "DISPLAY" db 8, "MMSYSTEM" db 0 ;end of table sEnd DATA ifdef DISABLE sBegin DATA ;ExternW wErrorOpts sEnd DATA endif ;public gpbeg, gpend sBegin CODE assumes CS,CODE externA __AHINCR externNP GetOwner externNP EntProcAddress externFP GetExePtr externFP SetSelectorLimit ;=============================================================== ; ; cProc IsBadReadPtr, ParmD lp ParmW cb cBegin beg_fault_trap BadRead1 les bx,lp ; check selector mov cx,cb jcxz ReadDone1 dec cx add bx,cx jc BadRead ; check 16 bit overflow mov al,es:[bx] ; check read permission, limit end_fault_trap ReadDone1: xor ax,ax ReadDone: cEnd BadRead1: fault_fix_stack BadRead: mov ax,1 jmp short ReadDone ;=============================================================== ; ; cProc IsBadWritePtr, ParmD lp ParmW cb cBegin beg_fault_trap BadWrite1 les bx,lp ; check selector mov cx,cb jcxz WriteDone1 dec cx add bx,cx jc BadWrite ; check 16 bit overflow or es:byte ptr [bx],0 ; check write permission, limit end_fault_trap WriteDone1: xor ax, ax WriteDone: cEnd BadWrite1: fault_fix_stack BadWrite: mov ax,1 jmp short WriteDone ;=============================================================== ; BOOL IsBadFlatReadWritePtr(VOID HUGE*lp, DWORD cb, WORD fWrite) ; This will validate a Flat pointer plus a special hack for Fox Pro ; to detect their poorly tiled selector (ie. all n selector with ; limit of 64K) (Our tiling is such that you can access up to end of ; the block using any one of the intermediate selectors flat) ; if we detect such a case we will fix up the limit on the first sel ; so GDI can access all of the memory as a 1st_sel:32-bit offset cProc IsBadFlatReadWritePtr, ParmD lp ParmD cb ParmW fWrite cBegin beg_fault_trap frp_trap les bx,lp ; check selector .386p mov eax,cb movzx ebx, bx test eax,eax ; cb == 0, all done. jz frp_ok add ebx,eax dec ebx cmp fWrite, 0 jne frp_write mov al,es:[ebx] ; read last byte jmp frp_ok frp_write: or byte ptr es:[ebx], 0 ; write last byte frp_ok: xor ax,ax end_fault_trap frp_exit: cEnd frp_trap: fault_fix_stack frp_bad: push ebx mov ecx, ebx ; get cb shr ecx, 16 ; get high word jecxz frp_bade ; if < 64K then bad ptr mov ax, es lsl eax, eax ; get limit on 1st sel jnz frp_bade ; bad sel? cmp ax, 0ffffh ; 1st of poorly tiled sels? jne frp_bade ; N: return bad ptr ; now we have to confirm that this is indeed the first of a bunch ; of poorly tiled sels and fix up the limit correctly of the first sel movzx ebx, ax ; ebx = lim total of tiled sels inc ebx ; make it 10000 mov dx, es frp_loop: add dx,__AHINCR ; next sel in array lsl eax, edx jnz frp_bade cmp ecx, 1 ; last sel? je @f ; if its not the last sel, then its limit has to be ffffh ; otherwise it probably is not a poorly tiled sel. cmp eax, 0ffffh jne frp_bade @@: add ebx, eax ; upd total limit inc ebx ; add 1 for middle sels loop frp_loop dec ebx ; take exact limit of last sel pop edx ; get cb cmp edx, ebx jg frp_bade_cleaned ; set limit of 1st sel to be ebx push es push ebx call SetSelectorLimit if KDEBUG mov ax, es krDebugOut DEB_WARN, "Fixing poorly tiled selector #AX for flat access" endif jmp frp_ok frp_bade: pop ebx frp_bade_cleaned: .286p mov ax,1 jmp frp_exit ;=============================================================== ; BOOL IsBadHugeReadPtr(VOID HUGE*lp, DWORD cb) ; cProc IsBadHugeReadPtr, ParmD lp ParmD cb cBegin beg_fault_trap hrp_trap les bx,lp ; check selector mov ax,off_cb mov cx,seg_cb mov dx,ax ; if cb == 0, then all done. or dx,cx jz hrp_ok sub ax,1 ; decrement the count sbb cx,0 add bx,ax ; adjust cx:bx by pointer offset adc cx,0 jc hrp_bad ; (bug #10446, pass in -1L as count) jcxz hrplast ; deal with leftover hrploop: mov al,es:[0ffffh] ; touch complete segments. mov dx,es add dx,__AHINCR mov es,dx loop hrploop hrplast: mov al,es:[bx] hrp_ok: xor ax,ax end_fault_trap hrp_exit: cEnd hrp_trap: fault_fix_stack hrp_bad: mov ax,1 jmp hrp_exit ;=============================================================== ; BOOL IsBadHugeWritePtr(VOID HUGE*lp, DWORD cb) ; cProc IsBadHugeWritePtr, ParmD lp ParmD cb cBegin beg_fault_trap hwp_trap les bx,lp ; check selector mov ax,off_cb mov cx,seg_cb mov dx,ax ; if cb == 0, then all done. or dx,cx jz hwp_ok sub ax,1 ; decrement the count sbb cx,0 add bx,ax ; adjust cx:bx by pointer offset adc cx,0 jc hwp_bad ; (bug #10446, pass in -1L as count) jcxz hwplast ; deal with leftover hwploop: or byte ptr es:[0ffffh],0 ; touch complete segments. mov dx,es add dx,__AHINCR mov es,dx loop hwploop hwplast: or byte ptr es:[bx],0 hwp_ok: xor ax,ax end_fault_trap hwp_exit: cEnd hwp_trap: fault_fix_stack hwp_bad: mov ax,1 jmp hwp_exit ;=============================================================== ; ; cProc IsBadCodePtr, ParmD lpfn cBegin beg_fault_trap BadCode1 mov cx,seg_lpfn lar ax,cx jnz BadCode ; Oh no, this isn't a selector! test ah, 8 jz BadCode ; Oh no, this isn't code! mov es,cx ; Validate the pointer mov bx,off_lpfn mov al,es:[bx] end_fault_trap xor ax, ax CodeDone: cEnd BadCode1: fault_fix_stack BadCode: mov ax,1 jmp short CodeDone ;======================================================== ; ; BOOL IsBadStringPtr(LPSTR lpsz, UINT cch); ; cProc IsBadStringPtr,, ParmD lpsz ParmW cchMax cBegin beg_fault_trap BadStr1 les di,lpsz ; Scan the string. xor ax,ax mov cx,-1 cld repnz scasb end_fault_trap neg cx ; cx = string length + 1 dec cx cmp cx,cchMax ja BadStr ; if string length > cchMax, then bad string. bspexit: cEnd BadStr1: fault_fix_stack BadStr: mov ax,1 jmp bspexit ;-----------------------------------------------------------------------; ; HasGPHandler ; ; ; ; See if GP fault handler is registered for faulting address. ; ; ; ; This scheme can only be used by registered modules. You register ; ; a module by adding an entry containing a length byte followed by ; ; the module name in the gp_valid_modules table defined above. ; ; ; ; Arguments: ; ; parmD lpFaultAdr ; ; ; ; Returns: ; ; AX = New IP of handler ; ; AX = 0 if no handler registered ; ; ; ; Error Returns: ; ; ; ; Registers Preserved: ; ; DI,SI,DS ; ; ; ; Registers Destroyed: ; ; AX,BX,CX,DX,ES ; ; ; ; Calls: ; ; GetOwner ; ; EntProcAddress ; ; ; ; The __GP table has the format of 4 words per entry, plus a ; ; zero word to terminate the table. The 'seg' value should be ; ; the actual selector (it must be fixed up by the linker), ; ; and the offset values should be relative to the start of the ; ; segment or group. The handler must be in the same code segment ; ; as the fault range (this ensures that the handler is present ; ; at GP fault time). ; ; ; ; __GP label word ; ; public __GP ; ; seg, offset begin, offset end, handler ; ; ... ; ; 0 ; ; ; ; The symbol '__GP' needs to be in the resident name table, so ; ; it should be added to the DEF file like this (with an ; ; appropriate ordinal value): ; ; ; ; EXPORTS ; ; __GP @??? RESIDENTNAME ; ; ; ; ; ; History: ; ; ?? Jun 91 Don Corbitt [donc] Wrote it ; ; 30 Jul 91 Don Corbitt [donc] Added support for __GP table ; ;-----------------------------------------------------------------------; cProc HasGPHandler,, ParmD lpfn cBegin cCall GetOwner, ; find owner of faulting code or ax, ax jz to_fail ;HH_fail lar bx, ax ; make sure segment is present jnz to_fail ;HH_fail test bx, 8000h jz to_fail ;HH_fail mov es, ax cmp es:[ne_magic], NEMAGIC jz @f to_fail: jmp HH_fail @@: ; check if the faulting module is allowed to use this scheme SetKernelDS mov di, es:[ne_restab] mov bx, di inc bx ; save ptr to module name xor cx,cx xor ax,ax mov si, offset gp_valid_modules mov al, es:[di] cld friend_or_fiend: mov cl, [si] jcxz HH_fail cmp al,cl jnz next_friend mov di, bx ; need to keep restoring di inc si ; skip len byte repe cmpsb jz we_know_this_chap dec si ; point to the mismatch next_friend: add si, cx inc si jmp short friend_or_fiend we_know_this_chap: xor cx, cx mov si, es:[ne_restab] ; restore si jmp short @F ; start in middle of code HH_nextSym: add si, cx ; skip name add si, 3 ; and entry point @@: mov cl, es:[si] ; get length of symbol jcxz HH_fail ; end of table - not found cmp cl, 4 ; name length jnz HH_nextSym cmp es:[si+1], '__' ; look for '__GP' jnz HH_nextSym cmp es:[si+3], 'PG' jnz HH_nextSym mov ax, es:[si+5] ; get ordinal for '__GP' if KDEBUG cCall EntProcAddress, else cCall EntProcAddress, ; I hate conditional assembly.... endif mov cx, ax or cx, dx jz HH_fail ; This shouldn't ever fail, but... lar bx, dx ; make sure segment is present jnz HH_fail test bx, 8000h jz HH_fail mov ds, dx mov si, ax mov ax, SEG_lpfn mov dx, OFF_lpfn next_fault_val: mov cx, [si] jcxz HH_fail cmp cx, ax ; does segment match? jnz gp_mismatch cmp [si+2], dx ; block start ja gp_mismatch cmp [si+4], dx ; block end jbe gp_mismatch mov ax, [si+6] ; get new IP jmp short HH_done gp_mismatch: add si, 8 jmp short next_fault_val HH_fail: xor ax, ax HH_done: cEnd ;======================================================================== ; ; BOOL IsSharedSelector(HGLOBAL h); ; ; Makes sure the given selector is shareable. Currently, we just check ; if it is owned by a DLL. We also need to check GMEM_SHARE bit but ; this isn't saved... ; cProc IsSharedSelector, ParmW sharedsel cBegin push sharedsel call GetExePtr or ax,ax ; bogus handle: exit. jz ISS_Done mov es,ax xor ax,ax test es:[ne_flags],NENOTP jz ISS_Done ; Not a DLL inc ax ; Yup a DLL ISS_Done: cEnd sEnd CODE end