← ST-Computer 06 / 1992

CHECKIT: Das Plus an Sicherheit

Programmierpraxis

Im Idealfall sollte ein Betriebssystem nach einem Programmabsturz dafür sorgen, daß es nicht zu Folgefehlern kommt, die einen kompletten Crash nach sich ziehen. Daß dies TOS nur selten gelingt, zeigt die Erfahrung. Durch einen kleinen Eingriff läßt sich die Stabilität des Systems nach einem Absturz erhöhen.

Doch zunächst sollte man sich Gedanken darüber machen, woran es denn liegen kann, daß nach einem abrupten Programmende häufig gar nichts mehr geht. Durch die Komplexität des GEM bedingt, ist die Wiederaufnahme des normalen Betriebs nach einem Absturz prinzipiell schwierig. Gerade in den TOS-Versionen ab TOS 1.04 wurde jedoch durch die Einführung des AES-Aufrufs wind_get eine Vorkehrung getroffen, die es dem GEM eher erlaubt, sich von den Folgen eines Absturzes zu erholen. Aber auch bei fehlerhaften TOS-Programmen beobachtet man, daß der Rechner nach einem Absturz oder spätestens nach dem Start des nächsten Programms eine Ruhepause einlegt, aus der man ihn nur durch Betätigung des Reset-Knopfes wieder erwecken kann. Schuld an diesem Verhalten sind häufig Systemvektoren, die von einem nicht residenten Programm verändert wurden und nach dessen Absturz ins Leere zeigen. Solange kein weiteres Programm gestartet wird, scheint alles im Lot zu sein, denn noch befindet sich der Code des ursprüngliches Programmes im Speicher. Wird nun aber ein neues Programm geladen, landet der Prozessor beim Sprung durch die Vektoren in einem Undefinierten Datenbereich, und der endgültige Absturz ist perfekt.

Resident oder nicht?

Somit wären also veränderte Vektoren als die eigentliche Ursache für viele System-Crashs ausgemacht. Das Verbiegen von Vektoren ist für die Funktion vieler Programme unvermeidlich, so daß es keinen Sinn macht, das Ändern von Systemvektoren prinzipiell zu unterlassen.

Aber dennoch: Diese Vektoren dürfen ausschließlich von residenten Programmen manipuliert werden, wobei das XBRA-Verfahren zu verwenden ist [1], Residente Programme verbleiben nach ihrer Beendigung im Speicher, so daß weiterhin definierter Code an der Einsprungadresse des Vektors steht.

Die eigentlichen Übeltäter sind somit die nicht residenten Programme, falls diese Vektoren verbiegen. Häufig geschieht dies lediglich dazu, beim Auftreten eines Bus- oder Adreßfehlers einen Absturz in Form von Bomben zu vermeiden und stattdessen eine entsprechende Meldung auszugeben, die den Anwender zwar beruhigen mag, aber das eigentliche Problem, nämlich den Programmfehler, nicht aus der Welt schafft.

Neben ungültigen Werten in den Exception-Vektoren, die ja nicht regelmäßig angesprungen werden, droht eine viel größere Gefahr dann, wenn ein Programm einen VBL-Slot belegt und abstürzt, ohne diesen wieder freigegeben zu haben. Da der Vertical Blank Interrupt regelmäßig auftritt, ist in solchen Fällen ein baldiger Absturz unvermeidlich. (Die Rechenkünstler unter den Lesern haben sicherlich schon ausgerechnet, daß es bis zum endgültigen Zusammenbruch bei einem Farbmonitor etwa 2.6x10'3 s länger dauert als bei Monochrom-Monitoren.)

Vektor-Check

Eigentlich liegt es auf der Hand, wie man dem geschilderten Problem zu Leibe rücken kann. Jedesmal dann, wenn ein Programm (auf welche Weise auch immer) beendet wird, müssen alle Systemvektoren und die Vektoren in der VBL-Queue überprüft werden. Zeigt ein Vektor in einen nicht mehr allozierten Bereich, liegt ein Fehler vor. Im Falle eines Eintrags in der VBL-Queue genügt es, diesen durch einen Null-Pointer zu löschen. Bei den restlichen Vektoren ist die Sachlage komplizierter, da diese ja von mehreren Programmen gleichzeitig benutzt werden können und man überdies nichts über deren ursprünglichen Wert aussagen kann. Hier ist man darauf angewiesen, daß alle vektorverbiegenden Programme das XBRA-Verfahren verwendet haben. Nur dann ist es möglich, den Vektor des unsanft beendeten Programms aus der Vektorkette auszuklinken, ohne daß andere Routinen davon beeinträchtig werden. Wurden Vektoren ohne Nutzung des XBRA-Mechanismus verbogen, bleibt lediglich die Möglichkeit, den ins Nirwana zeigenden Vektor vorsichtshalber auf einen RTE-Befehl umzulenken.

Natürlich muß man sich vor der Überprüfung der Systemvektoren vergewissern, daß ein Programm sich terminiert, ohne resident im Speicher zu verbleiben. Andernfalls wäre es natürlich falsch, verbogenene Vektoren, die in dieses Programm zeigen, zurückzusetzen. Nur bei nicht residenten Programmen ist ein Eingriff erlaubt.

CHECKIT

Die gestellten Forderungen erfüllt das Programm CHECKIT. Dabei ist die Systemvariable etv_term für unsere Anwendung sehr hilfreich. Dieser Vektor zeigt auf eine Routine, die nach der Beendigung jedes Programms angesprungen wird. Dies ist auch dann der Fall, wenn es sich um einen unvorhergesehenen Abbruch, beispielsweise durch einen Absturz, handelt.

Über den aktuellen Prozeß-Deskriptor läßt sich leicht feststellen, in welchem Speicherbereich TEXT, DATA und BSS-Segment dieses Programms lagen. Diese Informationen werden für die Entscheidung benötigt, ob ein Systemvektor in den Code- oder Datenbereich des Programms zeigt. Um welchen Programmtyp, also resident oder nicht resident, es sich handelt, erkennt CHECKIT anhand des letzten von diesem Programm abgesetzten GEMDOS-Aufrufs. Handelte es sich hierbei um PTERMRES, so bleibt der Programmcode resident im Speicher, und es ist nicht notwendig, irgendwelche Vektoren zu überprüfen. In allen anderen Fällen muß CHECKIT in Aktion treten.

Das hier vorgestellte Verfahren greift natürlich nur solange, wie ein Programm keine Vektoren in einen per MALLOC angeforderten Speicherblock legt. Prinzipiell wäre es zwar denkbar, auch bei MFREE-Aufrufen zu überprüfen, ob ein Vektor in den freigegebenen Speicherblock zeigt, aber leider benutzt das Desktop beim MFREE nach der Beendigung eines Programms nicht den GEMDOS-Vektor. Stattdessen wir direkt ins GEMDOS eingesprungen, so daß man hier nicht eingreifen kann Erst im MultiTOS wird sich dies ändern, da das Desktop dann lediglich noch eine GEM-Anwendung unter vielen darstellt, die für Systemaufrufe die TRAP-Vektoren benutzen muß.

Vektormanipulationen über das BIOS

Die genaue Funktionsweise von CHECKIT kann dem kommentierten Programm-Listing entnommen werden. Der Prozeß-Deskriptor läßt sich erst ab TOS 1.02 aus der _sysbase-Struktur entnehmen. Für TOS 1.00 muß man auf die inzwischen dokumentierte Adresse zurückgreifen, die sich im spanischen TOS von den restlichen länderspezifischen TOS-Versionen unterscheidet.

Daß CHECKIT die Installation der neuen Systemvektoren über die BIOS-Routine SETEXC erledigt und dazu nicht selber direkt auf die entsprechenden Adressen zugreift, hat übrigens einen guten Grund. Auf dem ST war es noch relativ sicher, TRAP-Vektoren durch direkten Zugriff auf die entsprechende Vektoradresse zu manipulieren, da diese Adresse konstant war. Bei anderen Prozessoren als dem 68000 sieht das jedoch anders aus. Hier kann der Beginn der Tabelle der Exception-Vektoren durch Manipulation des Vector-Base-Registers (VBR) an eine andere Stelle verlegt werden. Um sicherzugehen, daß man wirklich den gewünschten Vektor ändert, bleibt somit nur der Weg über das BIOS, das auch in zukünftigen TOS-Versionen, die das VBR nutzen könnten, den Vektor garantiert an die richtige Adresse schreibt.

Literatur:

[1] Jankowski, Rabich, Reschke, „Atari Profibuch ST-STE-TT", SYBEX-Verlag

***************************** * * * CHECKIT V1.0 * * Vektorcheek für ST und TT * * * * April 1992 by Uwe Seimet * * (c) 1992 MAXON Computer * * * ***************************** GEMDOS = 1 CCONWS = 9 PTERMRES = 49 MSHRINK = 74 BIOS = 13 SETEXC = 5 XBIOS = 14 KBDVBASE = 34 SUPEXEC = 38 etv_timer = $400 resvalid = $426 resvector = $42a _memtop = $436 nvbls = $454 _vblqueue = $456 _sysbase = $4f2 _p_cookies = $5a0 _ramtop = $5a4 _ramvalid = $5a8 ttmagic = $1357bd13 ;Magic für TT-PAM text move.l 4(sp),a0 move.l 12(a0),a6 ;Länge des ;TEXT-Segments add.l 20(a0),a6 ;Länge DATA add.l 28(a0),a6 ;Länge BSS lea $100(a6),a6 ;für Basepage pea (a6) pea (a0) clr -(sp) move #MSHRINK,-(sp) ;überzähligen trap #GEMDOS ;Speicher lea 12(sp),sp ;freigeben pea tttest(pc) move #SUPEXEC,-(sp) ;auf TT trap #XBIOS ;testen addq.l #6,sp pea gemdos(pc) move #33,-(sp) move #SETEXC,-(sp) ; neuer trap #BIOS ;GEMDOS-Vektor addq.l #8,sp move.l d0,o_gemdos pea term(pc) move #258,-(sp) move #SETEXC,-(sp) ;neuer Vektor trap #BIOS ;für etv_term addq.l #8,sp move.l d0,o_term pea message move #CCONWS,-(sp) trap #GEMDOS addq.l #6,sp clr -(sp) pea (a6) move #PTERMRES,-(sp) ;Programm trap #GEMDOS ;resident halten dc.l "XBRA" dc.l "USCK" o_term: dc.l 0 term: tst.b resflg ;nichts tun bne cont ;bei residentem Programm move.l _sysbase,a0 move.l a0,a1 move.l $28(a1),a0 move.l (a0),a0 ;Prozeß-Deskriptor cmp #$0102,$02(a1) ;neuer als bcc.s newtos ;TOS 1.00- move.l $873c,a0 ;Default für spanisches TOS 1.00 move $1c(a1),d0 ;Ländercode lsr #1,d0 ;isolieren emp #4,d0 ;spanisch? beq.s newtos ;ja- move.l $602c,a0 ;für TOS 1.00 newtos: move.l 8(a0),a1 ;Beginn des move.l a1,a2 ;TEXT-Segments add.l 12(a0),a2 ;+ Länge TEXT add.l 20(a0),a2 ;+ Länge DATA add.l 28(a0),a2 ;+ Länge BSS ;ergibt Endadresse move nvbls,d0 move.l _vblqueue,a0 bra.s checkvbl checkv: move.l (a0)+,d1 cmp.l d1,a1 ;zeigt Vektor bcc.s checkvbl ;in Programm- cmp.l d1,a2 ;bereich? bcs.s checkvbl clr.l -4(a0) ;Vektor checkvbl:dbra d0,checkv ;löschen lea $08,a0 moveq #8,d0 bsr.s check lea $80,a0 ;TRAP-Vektoren moveq #16,d0 bsr.s check lea $64,a0 ;Autovektor moveq #6,d0 ;Interrupts bsr.s check lea $0100,a0 ;MFP1- moveq #15,d0 ;Interrupts bsr.s check tst.b ttflg beq.s nott lea 320,a0 ;MFP2-, SCC- moveq #31,d0 ;Interrupts bsr.s check ;nur bei TT nott: lea $0114,a0 ;Systemtimer moveq #1,d0 bsr.s check ;und IKBD/MIDI lea etv_timer,a0 moveq #2,d0 bsr.s check ;etv-Vektoren movem.l a1-a2,-(sp) move #KBDVBASE,-(sp) trap #XBIOS addq.l #2,sp movem.l (sp)+,a1-a2 move.l d0,a0 moveq #8,d0 bsr.s check movem.l a1-a2,-(sp) dc.w $a000 move.l a2,a0 movem.l (sp)+,a1-a2 moveq #15,d0 ;LINEA- bsr.s check ;Vektoren cmp.l #$31415926,resvalid bne.s cont move.l resvector,a0 moveq #0,d0 bsr.s check cont: move.l o_term(pc),a0 ;weiter jmp (a0) ;im TOS *Testen, ob Vektor in ehemaligen Programrabereich *zeigt. Wenn ja, aus Vektorkette enfernen oder *auf RTE setzen. check: move.l #iret,d2 ;Zeiger move.l a0,a3 ;auf RTE trp: move.l (a3),d1 cmp.l #ttmagic,_ramvalid bne.s nottram ;kein TT-RAM- cmp.l _ramtop,d1 ;zeigt Vektor bcc.s invalid ;in TT-RAM? cmp.l #$01000000,d1 bcc.s test nottram: cmp.l _memtop,d1 ;zeigt Vektor bcc.s invalid ;in ST-RAM? cmp.l #$0400,d1 bcs.s invalid *Vektor ist gültig und zeigt in ST- oder TT-RAM test: cmp.l d1,a1 ;zeigt Vektor bcc.s valid ;in Programm- cmp.l d1,a2 ;bereich? bcs.s valid ;nein- move.l d1,a4 cmp.l #"XBRA",-12(a4) ;XBRA benutzt? bne noxbra move.l -4(a4),d2 ;neu verketten noxbra: move.l d2,(a3) valid: move.l d1,a3 cmp.l #"XBRA",-12(a3) beq trp invalid: addq.l #4,a0 ;nächster dbra d0,check ;Vektor rts *Auf TT testen tttest: move.l _p_cookies,d0 beq.s nojar move.l d0, a0 loop: movem.l (a0)+,d0-d1 tst.l d0 beq.s nojar cmp.l #"_ MCH",d0 bne loop cmp.l #$00020000,d1 ;TT? scc ttflg nojar: rts dc.l "XBRA" dc.l "USCK" o_gemdos:dc.l 0 gemdos: lea 8(sp),a0 ;Supervisor- btst #5,(sp) ;Modus? bne super ;ja- move.l usp,a0 super: cmp #PTERMRES,(a0) ;-residentes seq resflg ;Programm? move.l o_gemdos(pc),a0 jmp (a0) ;weiter im TOS iret: rte data message: dc.b $0d,$0a,"CHECKIT V1.0 installiert" dc.b $0d,$0a dc.b "1992 by Uwe Seimet",$0d,$0a,0 bss ttflg: ds.b 1 ;Flag für TT
Uwe Seimet