Our final solution achieves user-level DMA without the need of extra bits in the physical address. It is based on an idea proposed by Charek Dubnicki : If a process passes at least one shadow address more than once, then the DMA engine may be able to determine if a user process was interrupted by checking the two successive accesses to the same shadow address. Dubnicki's solution uses a three-instruction sequence as follows:
DMA(vsource, vdestination, size)
1: LOAD status1 FROM shadow(vsource)
2: STORE size TO shadow(vdestination)
3: LOAD status2 FROM shadow(vsource)
The DMA engine initiates a DMA transfer only if it sees a sequence of the form LOAD, STORE, and LOAD, and the address arguments of the first and third instruction in the sequence are the same. If a process is interrupted while trying to start a DMA, then the DMA engine will probably receive a non-valid sequence of shadow addresses, and no DMA operation will be initiated.
Figure 5: Possible interleaving in the 3-instruction Repeated passing of argument DMA approach. A malicious user is able to start a DMA and transfer its own data (C), into another process's address space (B).
Although seemingly correct, the above solution may lead to erroneous data transfers, if abused by malicious users. Assume, for example, that we have a legitimate process that wants to start a DMA, and a malicious process that wants to interfere. A possible interleaving of shadow accesses is shown in figure 5. In this interleaving, the instructions 1: to 3: reach the DMA engine, but no DMA operation is started. However, then next three instructions (4: to 6:) appear as a valid DMA sequence, and thus a DMA operation is started transferring data from address C to B, while the legitimate process wanted to transfer data from A to B.
Straightforward 4-instruction sequence extensions to the above solution remedy this situation. For example, assume the following obvious 4-instruction extension:
DMA(vsource, vdestination, size)If a malicious does not have any access to addresses vsource, and vdestination, then the above sequence seems to operate correctly. If however, the data contained in vsource are such that they can be read by any process in the system, then a malicious user may achieve the interleaving shown in figure 6. Suppose that the legitimate process wants to transfer data from A to B, and the malicious process has read-only access to A. In the interleaving shown in figure 6, the malicious process initiates the DMA transfer (in 4:), but the DMA engine tells the legitimate process that the DMA transfer was not initiated (in 5:). Such a behavior will probably lead several applications to erroneous behavior.
1: STORE size TO shadow(vdestination)
2: LOAD return_status1 FROM shadow(vsource)
3: STORE size TO shadow(vdestination)
4: LOAD return_status2 FROM shadow(vsource)
Figure 6: Possible interleaving in the 4-instruction Repeated passing of argument DMA approach. The malicious process starts the DMA (in 4:) but misinforms the legitimate process that the DMA did not start (in 5:).
Figure 7: User-level DMA by repeated passing of arguments.
To remedy the above limitation, we have developed a 5-instruction sequence that achieves user-level DMA. The shadow(vsource) address is passed twice to the DMA engine, while the shadow(vdestination) address is passed three times, as shown in figure 7. The DMA engine is prepared to receive 5-instruction sequences to shadow address space. The sequence should be of the form STORE, LOAD, STORE, LOAD, LOAD. If it sees anything out of this order, the DMA engine resets itself, waiting for the 5-instruction sequences. A DMA operation is started only if the DMA engine receives a sequence of the type STORE, LOAD, STORE, LOAD, LOAD, and the address arguments of instructions 1,3 and 5 are the same, and the address arguments of instructions 2 and 4 are the same as well.