Attached to this message are copies of new or modified routines for sstrace (a system service tracing program). The changes address the following problems: 1) In the return path from a system service call register R1 was not preserved; this is not important from the majority of system services, but routines run via sys$cm%%%% can return a value in R1. Tracing sys$system:decw$startlogin would cause the system to crash for this reason, for example. 2) The address translation buffer was not flushed after the page table entries for the system service vector table were modified. The system was only vulnerable when sstrace was deinstalled. Gary Nebbett Many comments have been deleted to save space. ---------------------------ssloadg.mar------------------------------ INTERCEPT:: MOVPSL R1 EXTZV #16, #5, R1, R0 BNEQ HIGHIPL EXTZV #22, #4, R1, R0 BNEQ STACKOK PROBEW #0, #512, -516(SP) BNEQ STACKOK HIGHIPL: POPL R0 ADDL2 #^X121FA, R0 JMP (R0) STACKOK: PUSHL AP CALLS #2, ENTER CALLG (AP), (R0) RETURN: PUSHL R1 PUSHL R0 CALLS #0, LEAVE POPL R0 POPL R1 RET ---------------------------sstracem.mar----------------------------- .LIBRARY /SYS$SHARE:LIB.MLB/ LKWSET_START: .ENTRY INVALIDATE_TB, ^M INVALIDATE_TB RET LKWSET_END: LKWSET_RANGE:: .LONG LKWSET_START, LKWSET_END ---------------------------sstrace.c-------------------------------- static void tidy() { unsigned long status; struct ssinfo *ssinfo; ssinfo = CTL$GL_SITESPEC; cmkrnl(restore_ptes, &status); if (status != SS$_WASSET) lib$stop(status); if (ssinfo->ss_vectors) { cmkrnl(unlock_vec, ssinfo, &status); if (status != SS$_WASSET && status != SS$_NORMAL) printf("Tidy unlock error %x\n", status); p1free(ssinfo->ss_vectors_size, ssinfo->ss_vectors); } if (ssinfo->ss_file) { cmkrnl(close_file, ssinfo->ss_file, &status); if (status != SS$_NORMAL) printf("Tidy file error %x\n", status); } if (ssinfo->ss_base) p1free(ssinfo->ss_base_size, ssinfo->ss_base); if (ssinfo->ss_info) p1free(ssinfo->ss_info_size, ssinfo->ss_info, &status); cmkrnl(clear_sitespec); } /* ----------------------------------------------------------------------- */ /* * These routines modify page tables; this may cause trouble with cached * virtual to physical address translations held in the hardware * "Translation Lookaside Buffer" (TLB), so we must flush this * buffer. See the INVALIDATE_TB macro in the VMS Device Support * Reference Manual for a description of this process. * * Failure to flush does not cause any problem when installing the * sstrace tables, as address references using the old mapping will * just address the intact system supplied vector table. However when * deinstalling sstrace, the memory occupied by the sstrace vector * tables is freed and is therefore liable to modification - this * could cause problems. */ /* * First make sure that the memory that will hold our modified vector * routines is paged aligned. Then set the PFN fields of the PTEs for * the P1 vector pages to have the same value as the PFN fields of the * pages holding our modified vector routines. * * We remember the original PTEs so that we can subsequently restore * them. * * The code to invalidate the TB is locked in the working set in accordance * with the advice given in the manual entry for the macro. As we are * changing part of the process private part of the virtual address space * which this process should never reference, I do not think that it is * necessary to protect the small window between the modification of the * PTEs and the flushing of the translation buffer. * * If an error occurs whilst locking the invalidate_tb code into the * working set, we eventually report this back to the user mode code, but * we do not let it stop us from completing the installation of sstrace. * This is because, as mentioned above, the only consequence of failing * to flush the TB is that sstrace will not take immediate effect, but * will remain dormant until the relevant address translations are * flushed, possibly as a result of a reschedule of the process. */ globalref unsigned long lkwset_range[2]; static unsigned long modify_ptes(struct ssinfo *ssinfo, unsigned long *status) { unsigned long *p1br, newpage, result[2]; struct valid_pte *oldpte, *newpte; int pages; *status = sys$lkwset(lkwset_range, result, PSL$C_KERNEL); newpage = (ssinfo->ss_vectors + 511) & 0xfffffe00; p1br = CTL$GL_PHD + PHD$L_P1BR; oldpte = (((P1SYSVECTORS - 0x40000000) / 0x200) * 4) + *p1br; newpte = (((newpage - 0x40000000) / 0x200) * 4) + *p1br; for (pages = 0; pages < (NUMVEC / 64); pages++) { ssinfo->ss_saved_pte[pages] = *oldpte; (oldpte++)->pfn = (newpte++)->pfn; } if (*status != SS$_WASCLR) return(SS$_NORMAL); invalidate_tb(); *status = sys$ulwset(lkwset_range, result, PSL$C_KERNEL); return(SS$_NORMAL); } /* * Because the user mode information block has already been copied * to the P1 buffer, we must pass the address of the P1 buffer to * modify_ptes; we do things in this order so that the switch of * vector tables remains the last major operaion performed by the * program. */ static void switch_ptes(struct ssinfo *ssinfo) { unsigned long status; cmkrnl(modify_ptes, ssinfo->ss_info, &status); if (status != SS$_WASSET) lib$stop(status); } /* * Restore the vectors by setting the P1 vector table PTEs to contain * the PFNs we saved when we started tracing. * * Most of the comments about the address translation buffer made above * modify_ptes() also apply here. * * One difference is that failure to lock the invalidate_tb() code into * memory means that we abort the run-down of sstrace. This is because * after changing the VA mappings we free the physical memory which * previously implemented part of the VA space. */ static unsigned long restore_ptes(unsigned long *status) { unsigned long *p1br, result[2]; struct valid_pte *oldpte; int pages; *status = sys$lkwset(lkwset_range, result, PSL$C_KERNEL); if (*status != SS$_WASCLR) return(SS$_NORMAL); p1br = CTL$GL_PHD + PHD$L_P1BR; oldpte = (((P1SYSVECTORS - 0x40000000) / 0x200) * 4) + *p1br; for (pages = 0; pages < NUMVECPAGES; pages++) (oldpte++)->pfn = CTL$GL_SITESPEC->ss_saved_pte[pages].pfn; invalidate_tb(); *status = sys$ulwset(lkwset_range, result, PSL$C_KERNEL); return(SS$_NORMAL); }