CHECKIT: Das Plus an Sicherheit

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
Aus: ST-Computer 06 / 1992, Seite 84

Links

Copyright-Bestimmungen: siehe Über diese Seite