home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!usc!sdd.hp.com!swrinde!network.ucsd.edu!mvb.saic.com!macro32
- From: raxco!galaxy.dnet!gleeve@uunet.UU.NET
- Newsgroups: vmsnet.internals
- Subject: Proposed methods for stealing DDT/FDT/etc.
- Message-ID: <9209102308.AA12245@relay1.UU.NET>
- Date: 10 Sep 92 21:21:31 GMT
- Organization: Macro32<==>Vmsnet.Internals Gateway
- Lines: 474
- X-Gateway-Source-Info: Mailing List
-
- Subj: RE: moving DDT for CDdriver etc.
-
- When building I/O intercept drivers to steal driver entry
- points, it is a problem to figure out how to tell a bit of intercept
- code where its' data is. If the intercept code is inside a device
- driver, one would like to find the driver UCB, but this is difficult
- in general. One can build a hash table of some sort, or perhaps
- search the I/O database for the unit of your intercept driver that
- points to the "victim" device, or use some otherwise unused UCB
- fields in the victim driver to point to the intercept UCB. These
- methods are either slow or vulnerable to collisions with other
- code, however.
- Paul Sorensen made the suggestion to put a DDT copy physically
- into the UCB of the intercept driver so the victim device UCB
- pointer ucb$l_ddt will now point to a known offset within the intercept
- UCB. This gives an apparently robust way of locating the intercept
- UCB for one interceptor. The present discussion has to do with some
- extensions to the idea so that multiple intercept drivers handling
- I/O to a single device can be handled.
- Such suggestions must be public and freely usable by anyone or
- they are useless. The suggestions here can be used for stealing
- FDT entries or start-io entries of normal drivers, and for other
- entry stealing where it is of any interest.
- While the code is not tested yet, I am offering it for general
- interest now rather than later; I expect to use it myself shortly
- and will correct any bugs I find. The ideas came from me, from Jon
- Pinkley, and from Paul.
-
- The idea of pointing ucb$l_ddt at a DDT that physically resides
- in a UCB of some other device like CDdriver is a wonderful way to
- make finding the corresponding device fast and reliable.
- It suffers from the disadvantage that it works for ONE driver
- only as proposed. That is, if I come along and try to steal the
- DDT again and happen to use the original driver's UCB$L_DDT
- link, the new DDT winds up in MY UCB, and CDdriver will find the
- DDT in the wrong place.
- The example code here involves some chaining, but should scale
- well and provide fairly fast access to intercept data, while not
- breaking every time a second unrelated application tries to patch
- the same driver structures.
-
- Where a DDT or any of its fields need to be modified, the suggestion
- here is to put a copy of that DDT into the UCB of the intercept driver
- that holds the code that is being inserted. Using a driver for such
- patched code makes it very simple to track where the code is, and the
- UCB of the driver provides a convenient spot for locating data that
- must be kept track of for each unit. Process-specific or channel
- specific structures' listheads can also be kept there.
- This note is offered to the public in the hope that a standard
- can be worked out, or that it can be used as one, as is, so that
- code that must steal DDTs or FDTs can interoperate no matter who
- the author is. So far Jon Pinkley and Glenn Everhart have contributed
- to the macro32-list discussions on this, and the code examples are
- provided by one of us (gce) to assist in the use of the ideas.
-
- The examples would live as pieces of a device driver for some
- intercept device. The relevant parts of UCB extension definitions,
- of the DPT_STORE macro, and the bits of code are provided here.
- These bits of code serve to steal a DDT from some victim driver and
- place it in the UCB of the intercept device, to locate the intercept
- UCB when executing intercept code with R5 pointing to the victim
- device's UCB, and to remove an intercepted DDT from the chain of
- these when it is necessary to disconnect. The FDT code replacement
- is embedded in the example code, so it can be seen how such FDT
- additions can be made using these methods. In practice the DDT and
- the FDT are obtained via the UCB pointer, which is why the DDB
- pointer is left intact.
-
- NOTE!!!
-
- THIS CODE IS ALPHA EXAMPLES at this point, not final and tested
- code. It is intended to be approximately correct, but another note
- will be issued when it is tested. In particular it needs to have
- "XX" replaced by proper device name characters here and there,
- and needs several more $xxxDEF calls. It may also need assorted
- bugfixes.
-
-
- The notion is to produce a UCB extension that looks like this:
-
- .long ident-pattern ; unique to driver that owns this ucb
- .long intercept-DDT-address; This is modified by another intercept
- ; driver that intercepts the victim UCB
- ; after we do. This allows us to find
- ; the our interceptor's DDT.
- .long previous-DDT-address ; where to find the next startio addr
- .long intercept-signature ; same for all intercept drivers
- ucb$a_victimddt:
- .blkb ddt$k_length ; note this changed in V5.5
-
- As a particular example, one might have a UCB extension that would look
- like this:
-
- $ucbdef
-
- $defini UCB
- .=ucb$k_lcl_disk_length
- ; other $def macros as desired
- ;
- ; DDT intercept fields
- ; following must be contiguous.
- $def ucb$l_uniqid .blkl 1 ;driver-unique ID, gets filled in
- ; by DPT address for easy following
- ; by SDA
- $def ucb$l_intcddt .blkl 1 ; Our interceptor's DDT address if
- ; we are intercepted
- $def ucb$l_prevddt .blkl 1 ; previous DDT address
- $def ucb$l_icsign .blkl 1 ; unique pattern that identifies
- ; this as a DDT intercept block
- $def ucb$a_vicddt .blkb ddt$k_length
- ; space for victim's DDT
-
- $defend UCB
-
- ; Then at the DPT_STORE macros, some like this after the
- ; DPT_STORE REINIT call.
-
- DPT_STORE UCB,UCB$L_UNIQID,L,XX$DPT ;store DPT address
- ; (change "XX" to device
- ; mnemonic correct values)
- DPT_STORE UCB,UCB$L_ICSIGN,L,X^F012F024 ; Add unique pattern (that might
- ; bring back some memories in
- ; DOS-11 users)
-
- ; HISTORICAL NOTE: under DOS-11, one would get F012 and F024 errors
- ; on odd address and illegal instruction traps. If we don't have
- ; this magic number HERE, on the other hand, we're likely to see
- ; bugchecks in VMS due to uncontrolled bashing of UCB fields!
-
- The driver dispatch table points at various driver addresses. If
- this sort of thing is used, one should be aware that reloads of
- the victim driver will cause trouble, and reloads of intercept
- drivers can. It is probably wise to inhibit reloading of drivers
- whose DDTs are moved by this sort of code. The same is probably wise
- for intercept drivers unless they can be VERY careful about their
- FDT fields, and those of anyone who has stolen them, and about
- other entries, when they are reloaded. (If I reset the DDT FDT
- pointer (ddt$l_fdt) to point into my intercept driver, someone
- intercepts that and gets a pointer to my FDT code, and then I
- reload my intercept driver and move the FDT code, clearly the
- external pointer to me can cause problems. I can however unlink
- everything and relink later if my driver is reloaded.)
-
-
- With such a standard in force, if I find the DDT from ucb$l_ddt
- of the victim device, I can check ident-pattern against my
- own program's pattern, and if it matches I just go ahead and use
- offsets from there to find my driver's UCB and thus its data
- (e.g. the CDdriver UCB). If it does not match, I chain using
- the previous-DDT-address and keep looking for my pattern, and
- when I find it (in usually one or two tries at most), I have
- found my UCB and can go on from there.
- The pattern choice is arbitrary, but in this example is set
- to the DPT address since that makes SDA dumps show the intercept
- driver name, facilitating its use.
- For such a standard to work, it must be used by anyone stealing
- the DDT for whatever purpose. This note is written in the hope this
- will be done and will impact user code as little as possible.
-
- I would suggest a similar strategy be used for stealing
- FDT table bits also. These can be made to coexist rather nicely
- if one has a structure somewhat along these lines for FDT routines
- to be added to a driver's FDT chain. In the examples here, the
- FDT chain just gets added to automatically in virtue of moving
- the DDT and chains "by itself" because that is the nature of the
- FDT scan.
-
- Note the identifier isn't really needed for functions but can be used
- for manipulations of FDT code if the FDT pieces live outside a driver.
- Inside a driver they can be given the value of the DPT address as in
- the example code here.
-
- ; offsets to FDT chaining data
- fdt_prev = -4
- fdt_idnt = -8
- .long ident-pattern; unique for each application
- .long old-fdt-table-address-from-before-patch
- xx_functable:
- newfdt: functab ,<mask for disk legal functions>
- functab ,<mask for disk buffered functions>
- functab myswitch,<mask of all 1's>
- myfdtbgn=.
- functab mypatch1,<mask>
- functab mypatch2,<mask>
- ...
- functab mypatchn,<mask>
- myfdtend=.
- functab to_orig,<mask of all 1's>
-
- ; myswitch -
- ; Based on state of "myonoff" variable either enable or disable
- ; my FDT processing, allowing the FDT chain to remain always intact.
- ; This needs to be the first of a chain of FDT entries added to the
- ; FDT processing of a driver.
- myswitch:
- tstl myonoff
- bneq 1$
- rsb
- 1$: addl2 #<myfdtend-myfdtbeg>,r8
- rsb
-
- ; to_orig -
- ; This entry continues FDT processing at the point after the new
- ; entries by returning to the original FDT chain at the point where
- ; that chain begins. (It is presumed that FDT entries will always be
- ; added ahead of existing ones due to the nonreturning nature of
- ; FDT processing.) This is done instead of simply duplicating the
- ; DEC FDT entries because in this way multiple FDT patches can
- ; coexist, as would be impossible if this trick were not used. As
- ; can be seen, its overhead is minimal.
- to_orig:
- movl newfdt+fdt_prev,r8 ;point to original FDT point
- addl2 #<16-12>,r8 ;pass the 2 entry masks
- ;back up since sysqioreq adds 12
- rsb ;off to the previous FDT routines.
- myonoff: .long 0 ;my global switch, can be before or
- ;after the rest, doesn't matter since only
- ;my code knows about it.
-
-
-
-
-
- Now in order for an intercept driver to steal entries from
- a victim driver, it would use code something like the following,
- in kernel mode. The initialization is outside the scope of the
- discussion.
-
- ; steal DDT from host. Assumes that the intercept UCB address
- ; is in R5 (that is, the UCB in which we will place the DDT copy),
- ; and that the UCB of the device whose DDT we are stealing is
- ; pointed to by R11. All registers are preserved explicitly so that
- ; surrounding code cannot be clobbered. R0 is returned as a status
- ; code so that if it returns with low bit clear, it means something
- ; went wrong so the bash did NOT occur. This generally means some other
- ; code that does not follow this standard has grabbed the DDT already.
- ; The following example assumes the code lives in a driver so the
- ; unique ID field and magic number are set already.
-
- pushr #^m<r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>
- ; Acquire victim's fork lock to synchronize all this.
- movl #ss$_normal,r0 ;assume success
- forklock lockaddr=ucb$b_flck(r11),-
- savipl=-(sp),preserve=YES
- ; find the current DDT address from the UCB (leaving the copy in
- ; the DDB alone)
- movl ucb$l_ddt(r11),r10 ;point at victim's DDB
- ; see if this DDT is the same as the original
- movl ucb$l_ddb(r11),r9 ;the ddb$l_ddt is the original
- cmpl ddb$l_ddt(r9),r10 ;bashing driver the first time?
- beql 1$ ;if eql yes
- ; driver was bashed already. Check that the current basher followed the
- ; standard. Then continue if it looks OK.
- p.magic=x^F012F024
- cmpl <ucb$l_icsign-ucb$a_vicddt>(r10),#p.magic
- ;does the magic pattern exist?
- ; if magic pattern is missing things are badly messed.
- beql 2$ ;if eql looks like all's well
- movl #2,r0 ;say things failed
- brw 100$ ;(brb might work too)
- 2$:
- ; set our new ddt address in the previous interceptor's slot
- movab ucb$a_vicddt(r5),<ucb$l_intcddt-ucb$a_vicddt>(r10)
- ;store next-DDT address relative
- ;to the original victim one
- 1$:
- movl r10,ucb$l_prevddt(r5) ;set previous DDT address up
- clrl ucb$l_intcddt(r5) ;clear intercepting DDT initially
- 3$:
- pushl r5
- movc3 #ddt$k_length,(r10),ucb$a_vicddt(r5) ;copy the DDT
- popl r5 ;get UCB pointer back (movc3 bashes it)
- ;
- ; Here make whatever mods to the DDT you need to.
- ;
- ; FOR EXAMPLE make the following mods to the FDT pointer
- ; (These assume the standard proposed for FDT pointers)
- movab ucb$a_vicddt(r5),r8 ;get a base register for the DDT
- movl ddt$l_fdt(r10),xx_functable+fdt_prev ;save old FDT address
- movl ucb$l_uniqid(r5),xx_functable+fdt_idnt ;save unique ID also
- movab xx_functable,ddt$l_fdt(r8) ;point at our FDT table
- clrl myonoff ;turn my FDTs on
- ;
- ; Finally clobber the victim device's DDT pointer to point to our new
- ; one.
- movab ucb$a_vicddt(r5),ucb$l_ddt(r11)
- ; Now the DDT used for the victim device unit is that of our UCB
- ; and will invoke whatever special processing we need. This processing in
- ; the example here causes the intercept driver's FDT routines to be
- ; used ahead of whatever was in the original driver's FDTs. Because
- ; the DDT is modified using the UCB pointer only, target device units
- ; that have not been patched in this way continue to use their old
- ; DDTs and FDTs unaltered.
- ;
- ; Processing complete; release victim's fork lock
- 100$:
- forkunlock lockaddr=ucb$b_flck(r11),newipl=(sp)+,-
- preserve=YES
- popr #^m<r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11>
-
-
-
- To locate the intercept UCB address from the DDT address, it is
- best to hold the forklock, so that in case someone tries to
- modify the list the lookups will not get clobbered. This code
- can be run from basically any desired IPL at start inside an
- intercept driver (DEC does it at fdt time in some examples).
- Note that (omitting the error check code) the search loop
- is only
-
- 2$: cmpl ...
- beql 1$
- movl ...
- brb 2$
- 1$:
-
- which is really quite efficient.
-
-
- If we are in a bit of code that runs in an intercept driver, something
- like the following sequence could locate the intercept driver UCB
- and return its address in R11, assuming the standard above has been
- followed:
-
- ; Assumes that R5 is the UCB address of the device that has had some
- ; code intercepted and that we are in some bit of code that knows
- ; it is in an intercept driver. Also assumes R11 may be used as
- ; scratch registers (as is true in FDT routines). Control returns at
- ; label "err" if the DDT appears to have been clobbered by
- ; something not following this standard, if conditional "chk.err"
- ; is defined.
- ; Entry: R5 - victim device UCB address
- ; Exit: R11 - intercept driver UCB address
-
- pushl r10
- movl ucb$l_ddt(r5),r10 ;get the DDT we currently have
- movab xx$dpt,r11 ;magic pattern is DPT addr.
- ; lock this section with forklock so we can safely remove
- ; entries at fork also. Use victim device forklock.
- forklock lock=ucb$b_flck(r5),savipl=-(sp),preserve=YES
- 2$: cmpl <ucb$l_uniqid-ucb$a_vicddt>(r10),R11
- ;this our own driver?
- beql 1$ ;if eql yes, end search
- .if df,chk.err
- p.magic=x^F012F024
- cmpl <ucv$l_icsign-ucb$a_vicddt>(r10),#p.magic
- bneq err ;exit if this is nonstd bash
- .endc ;chk.err
- ; follow DDT block chain to next saved DDT.
- movl <ucb$l_prevddt-ucb$a_vicddt>(r10),r10
- ;point R10 at the next DDT in the
- ;chain
- .if df,chk.err
- bgeq err ; (error check if not negative)
- .endc ;chk.err
- brb 2$ ;then check again
- 1$:
- ; At this point R10 contains the DDT address within the intercept
- ; driver's UCB. Return the address of the intercept driver's UCB next.
- movab <0-ucb$a_vicddt>(r10),r11 ;point R11 at the intercept UCB
- forkunlock lock=ucb$b_flck(r5),newipl=(sp)+,preserve=YES
- popl r10
-
- ; xx$dpt addr is appl magic number, and the name must be defined
- ; within the intercept driver. (replace "xx" by the appropriate
- ; characters.)
-
-
-
- To remove an entry from this DDT chain, we must synchronize again at
- fork IPL. The back chain can then be used to replace the entry in the
- UCB with the one needed. If we assume we enter with R5 as the victim
- driver UCB address, an entry can be removed like this:
- ;
- ; Entry: R5 points at victim device UCB and current driver is the one
- ; desiring to remove its entry from the DDT chain. Thus its xx$dpt: address
- ; is the one being sought. ("Current driver" here means the intercept
- ; driver.)
- ; It is assumed that the driver knows that the DDT chain was patched
- ; so that its UCB contains an entry in the DDT chain
- pushr #^m<r0,r1,r2,r3,r10,r11>
- movl ucb$l_ddt(r5),r10 ;get the DDT we currently have
- movl ucb$l_ddb(r5),r1 ;get ddb of victim
- movl ddb$l_ddt(r1),r1 ;and real original DDT
- movl r10,r0 ;save ucb$l_ddt addr for later
- movab xx$dpt,r11 ;magic pattern is DPT addr.
- ; lock this section with forklock so we can safely remove
- ; entries at fork also. Use victim device forklock.
- forklock lock=ucb$b_flck(r5),savipl=-(sp),preserve=YES
- 2$: cmpl <ucb$l_uniqid-ucb$a_vicddt>(r10),R11
- ;this our own driver?
- beql 1$ ;if eql yes, end search
- .if df,chk.err
- p.magic=x^F012F024
- cmpl <ucv$l_icsign-ucb$a_vicddt>(r10),#p.magic
- bneq err ;exit if this is nonstd bash
- .endc ;chk.err
- ; follow DDT block chain to next saved DDT.
- movl <ucb$l_prevddt-ucb$a_vicddt>(r10),r10
- ;point R10 at the next DDT in the
- ;chain
- .if df,chk.err
- bgeq err ; (error check if not negative)
- .endc ;chk.err
- brb 2$ ;then check again
- 1$:
- ; At this point R10 contains the DDT address within the intercept
- ; driver's UCB. Return the address of the intercept driver's UCB next.
- tstl <ucb$l_intcddt-ucb$a_vicddt>(r10) ;were we intercepted?
- bgeq 3$ ;if geq no, skip back-fixup
- ; we were intercepted. Fix up next guy in line.
- movl <ucb$l_intcddt-ucb$a$_vicddt>(r10),r11 ;point at interceptor
- movl <ucb$l_prevddt-ucb$a_vicddt>(r10),<ucb$l_prevddt-ucb$a_vicddt>(r
- 11)
- ;copy our prior DDT ptr to next one
-
- 3$:
- ; if we intercepted someone, fix up our intercepted victim to skip by
- ; us also.
- movl <ucb$l_prevddt-ucb$a_vicddt>(r10),r2 ;did we intercept
- ;original driver?
- cmpl r2,r1 ;test if this is original
- beql 5$ ;if eql yes, no bash
- ; replace previous intercept address by ours (which might be zero)
- movl <ucb$l_intcddt-ucb$a$_vicddt>(r10),<ucb$l_intcddt-ucb$a_vicddt>(
- r2)
- 5$:
- ; Here remove FDT entries from the list if they were modified.
- ; This needs a scan of the FDT chain starting at the victim's
- ; ddt$l_fdt pointer and skipping around any entry that has address
- ; xx_functable:
- ; The FDT chain is singly linked. The code here assumes everybody
- ; plays by the same rules!
- ; NOTE: Omit this code if we didn't insert our FDT code in the chain!!!
- movl ddt$l_fdt(r0),r1 ;start of FDT chain
- movab xx_functable,r2 ;address of our FDT table
- clrl r3
- 6$: cmpl r1,r2 ;current fdt point at us?
- beql 7$ ;if eql yes, fix up chain
- movl r1,r3 ;else store last pointer
- movl fdt_prev(r1),r1 ;and point at next
- bgeq 8$ ;if not sys addr, no messin'
- brb 6$ ;look till we find one.
- 7$:
- ;r3 is 0 or fdt pointing to our block next
- ;r1 points at our fdt block
- tstl r3 ;if r3=0 nobody points at us
- bgeq 8$ ;so nothing to do
- movl fdt_prev(r1),fdt_prev(r3) ;else point our next-fdt
- pointer at
- ;last fdt addr.
- 8$:
- ;
- ; Finally if the victim UCB DDT entry points at ours, make it point at
- ; our predecessor. If it points at a successor, we can leave it alone.
- cmpl r10,r0 ;does victim ucb point at our DDT?
- bneq 4$ ;if not cannot replace it
- movl <ucb$l_prevddt-ucb$a_vicddt>(r10),ucb$l_ddt(r5)
- 4$:
- forkunlock lock=ucb$b_flck(r5),newipl=(sp)+,preserve=YES
- popr #^m<r0,r1,r2,r3,r10,r11>
-
-
- A final note:
- The code here assumes that its standards are followed by all DDT
- bashing / FDT bashing. System crashes can occur if this is not so. But
- note: they do now. It is not possible to guard against kernel mode
- code that bashes vectors in Attila-the-Hun mode. Some bits more checking
- can be done for production code, but I hope we can come to closer accord
- over how these operations can coordinate.
-
- Glenn Everhart
- Everhart@Raxco.com
- 215 358 5875
-