_STALKING GENERAL PROTECTION FAULTS: PART I_ by Andrew Schulman [LISTIN ONE] / FFFF.C -- causes GP fault in real mode on 286/386, and in Virtual 86 mode -- catch it in real mode -- can't catch it in Virtual 86 mode Turbo C: tcc ffff.c Microsoft C: cl ffff.c */ #include #include #include "gpfault.h" void (interrupt far *old)(); void fini(char *msg, int exit_code) { puts(msg); _dos_setvect(INT_GPFAULT, old); exit(exit_code); } void far my_exit(void) { fini("Bye!", 1); } void interrupt far handler(REG_PARAMS r) { printf("\nProtection violation at %04X:%04X\n", r.cs, r.ip); /* change CS:IP on stack so control is "returned" to my_exit */ /* this is an alternative to using longjmp() */ r.cs = FP_SEG(my_exit); r.ip = FP_OFF(my_exit); } main() { int *p = (int *) -1; old = _dos_getvect(INT_GPFAULT); #ifndef CRASH_AT _dos_setvect(INT_GPFAULT, handler); #endif printf("int at %p is ", p); printf("%04X\n", *p); /*NOTREACHED on 286/386 */ fini("Done!", 0); } [LISTIN TWO] / GPFAULT.H -- REG_PARAM structure represents stack at entry to interrupt handler -- CPU pushes flags, CS:IP, and, for protected-mode INT 08-0D, an error code -- Compiler pushes all other registers at entry to interrupt function -- Turbo C pushes registers in a strange order -- Watcom C 386 7.0 also pushes FS and GS registers (unfortunately MetaWare High C for MS-DOS 386 1.5 does not) -- replace Microsoft C 5.1 FP_SEG, FP_OFF macros with ones that don't requires lvalues -- keep Watcom C 386 7.0 FP_SEG, etc. -- these work for 48-bit pointers -- for MetaWare High C for 386 MS-DOS, need our own 48-bit FP_SEG, etc. */ #ifdef InstantC_16M /* Rational Systems Instant-C/16M protected-mode C interpreter */ #define DOS16M #define PROT_MODE #endif typedef struct { #if defined(__WATCOMC__) && defined(__386__) unsigned gs,fs; #endif #ifdef __TURBOC__ unsigned bp,di,si,ds,es,dx,cx,bx,ax; #else unsigned es,ds,di,si,bp,sp,bx,dx,cx,ax; /* same as PUSHA */ #endif #ifdef PROT_MODE unsigned err_code; /* for pmode INT 08-0D */ #endif unsigned ip,cs,flags; } REG_PARAMS; #ifdef __TURBOC__ #define _dos_setvect(x,y) setvect(x,y) #define _dos_getvect(x) getvect(x) #endif #define INT_GPFAULT 0x0D /* 386 protected-mode: far pointer is 48 bits; near pointer is 32 bits */ /* thus, 386 pmode near pointer can hold a real-mode far pointer */ #ifdef __HIGHC__ #define real_far _near #define prot_far _far #define far _far #else #if defined(__WATCOMC__) && defined(__386__) #define real_far near #define prot_far far #endif #endif #ifdef __HIGHC__ /* use overlay struct: no High C support for 48-bit immediate values */ /* remember that unsigned is 32 bits, short is 16 bits */ typedef struct { unsigned off; short seg; } overlay; #define FP_SEG(fp) ((overlay prot_far *) &(fp))->seg #define FP_OFF(fp) ((overlay prot_far *) &(fp))->off #else #if (!(defined(__WATCOMC__) && defined(__386__))) /* Microsoft C FP_SEG() and FP_OFF() require an lvalue: yuk! */ #ifdef FP_SEG #undef FP_SEG #undef MK_FP #undef FP_OFF #endif #define FP_SEG(fp) (((UL)(fp)) >> 16) #define MK_FP(seg,off) ((FP)(UL)(((UL)(seg) << 16) | (off))) #define FP_OFF(fp) ((unsigned)(fp)) #endif #endif typedef unsigned long UL; typedef void far *FP; typedef enum { FALSE, TRUE } BOOL; #ifdef __HIGHC__ #pragma Calling_convention(C_interrupt | _FAR_CALL); typedef void (*IPROC)(); #pragma Calling_convention(); #else typedef void (interrupt far *IPROC)(); #endif [LISTIN THREE] / GPFAULT.C -- for AI Architects OS/286 or Rational Systems DOS/16M for AI Architects: cl -AL -Ox -Gs2 -c -DPROT_MODE gpfault.c link gpfault,gpfault,gpfault/map,\os286\llibce; \os286\express gpfault cp gpfault for DOS16M: if not exist dos16lib.obj cl -AL -Ox -Gs2 -c source\dos16lib.c cl -AL -Ox -Gs2 -c -DPROT_MODE -DDOS16M -Zi gpfault.c link /co preload crt0_16m pml gpfault dos16lib /noe,gpfault; makepm gpfault splice gpfault gpfault d gpfault */ #include #include #include #include #include #ifdef DOS16M #include "dos16.h" #endif #include "gpfault.h" #define IN_MY_CODE 11593 #define IN_USER_CODE 16843 #define IN_HANDLER 40311 unsigned whereami = IN_MY_CODE; /* need our own protection for this */ jmp_buf toplevel; jmp_buf toplevel_copy = {0}; /* initialized, in a different segment */ unsigned legal = 0; /* just a legal address to bang on */ void (interrupt far *old_int13handler)(); void interrupt far int13handler(REG_PARAMS r); /* GP fault handler */ void goto_toplevel(void); /* longjump out of handler */ void revert(void); /* restore default handler */ void fail(char *msg, FP fp); /* fail by calling default handler */ main(int argc, char *argv[]) { char buf[255]; unsigned far *fp; unsigned data; old_int13handler = _dos_getvect(INT_GPFAULT); _dos_setvect(INT_GPFAULT, int13handler); printf("'Q' to quit, '!' to reinstall default GP Fault handler\n"); printf("%Fp is a legal address to poke\n", &legal); /* next line helps illustrate limitations of protection */ printf("%Fp is not a legal address to poke\n", &legal-1); setjmp(toplevel); whereami = IN_MY_CODE; memcpy(toplevel_copy, toplevel, sizeof(jmp_buf)); for (;;) { printf("$ "); *buf = '\0'; gets(buf); if (toupper(*buf) == 'Q') break; else if (*buf == '!') { revert(); continue; } sscanf(buf, "%Fp %u", &fp, &data); whereami = IN_USER_CODE; *fp = data; /* the crucial line of code */ printf("poked %Fp with %u\n", fp, *fp); whereami = IN_MY_CODE; } revert(); puts("Bye"); return 0; } void revert(void) { _dos_setvect(INT_GPFAULT, old_int13handler); } void fail(char *msg, FP fp) { (fp) ? printf(msg, fp) : puts(msg); revert(); _chain_intr(old_int13handler); } void goto_toplevel(void) { if (memcmp(toplevel, toplevel_copy, sizeof(jmp_buf)) == 0) longjmp(toplevel, -1); else fail("Toplevel context has been trompled", 0); } void interrupt far int13handler(REG_PARAMS r) { switch (whereami) { case IN_HANDLER: fail("\nDouble fault at %Fp\n", MK_FP(r.cs, r.ip)); /*NOTREACHED*/ case IN_MY_CODE: fail("\nInternal error at %Fp\n", MK_FP(r.cs, r.ip)); /*NOTREACHED*/ case IN_USER_CODE: whereami = IN_HANDLER; _enable(); /* reenable interrupts */ /* we could use Intel LAR and LSL instructions here to figure out why GP fault took place: did we try to write into code? or did offset overrun segment limit? */ printf("\nProtection violation at %04X:%04X\n", r.cs, r.ip); if (r.err_code) printf("Error code %04X\n", r.err_code); printf(" \n", r.es, r.ds, r.di, r.si); printf(" \n", r.ax, r.bx, r.cx, r.dx); goto_toplevel(); /*NOTREACHED*/ default: whereami = IN_HANDLER; _enable(); puts("whereami flag got trompled"); goto_toplevel(); /*NOTREACHED*/ } } #ifdef DOS16M void _dos_setvect(unsigned intno, IPROC isr) { D16pmInstall(intno, FP_SEG(isr), FP_OFF(isr), NULL); } IPROC _dos_getvect(unsigned intno) { IPROC isr; D16pmGetVector(intno, (INTVECT *) &isr); return isr; } #endif Exampl 1 Thi cod wil caus G faul i protecte mod main() { int far *fp = (int far *) main; *fp = rand(); main(); } Exampl 2 I thi progra G faults it' becaus o a erro b th user no b th applicatio itself. main(int argc, char *argv[]) { int far *fp = (int far *) atol(argv[1]); *fp = atoi(argv[2]); } Figur 1 G faul dump Session Title: UR/Forth SYS1943: A program caused a protection violation. TRAP 000D AX=2092 BX=0000 CX=FFFF DX=3FC2 BP=FFFA SI=05EA DI=05E4 DS=00C7 ES=0000 FLG=2206 CS=0227 IP=2093 SS=00C7 SP=FBFC MSW=FFED CSLIM=FFFE SSLIM=FFFF DSLIM=FFFF ESLIM=**** CSACC=DF SSACC=F3 DSACC=F3 ESACC=** ERRCD=0000 ERLIM=**** ERACC=** End the program Figur 2 GPFAULT.EX session C:\DOS16M>gpfault DOS/16M Protected Mode Run-Time Version 3.25 Copyright (C) 1987,1988,1989 by Rational Systems, Inc. 'Q' to quit, '!' to reinstall default GP fault handler 00A0:04C6 is a legal address to poke 00A0:04C4 is not a legal address to poke $ 1234:5678 666 Protection violation at 0088:00C5! Error code 1234 $ 00A0:04C4 666 poked 00A0:04C4 with 666 $ 00A0:04C2 1 poked 00A0:04C2 with 1 $ 0088:00C5 666 Protection violation at 0088:00CB! $ 0:0 0 Protection violation at 0088:00CB! $ ! $ 0:0 0 DOS/16M: Unexpected Interrupt=000D at 0088:00CB code=0000 ss=00A0 ds=00A0 es=0000 ax=0000 bx=0000 cx=0015 dx=0000 sp=1982 bp=1A92 si=0082 di=1AC0 C:\DOS16M>