; 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

;===============================================================
;
;
cProc   IsBadReadPtr,<PUBLIC,FAR,NONWIN>
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,<PUBLIC,FAR,NONWIN>
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 IsBadHugeReadPtr(VOID HUGE*lp, DWORD cb)
;
cProc   IsBadHugeReadPtr,<PUBLIC,FAR,NONWIN>
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,<PUBLIC,FAR,NONWIN>
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,<PUBLIC,FAR,NONWIN>
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,<PUBLIC,FAR,NONWIN>,<DI>
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,<PUBLIC,FAR,NONWIN>,<ds,si,di>
ParmD	lpfn
cBegin
	cCall	GetOwner, <SEG_lpfn>	; 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,<es,ax,1>
else
	cCall	EntProcAddress,<es,ax>	; 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,<PUBLIC,FAR,NOWIN>
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
