Assignment 3, Solutions

Part of the homework for 22C:169, Spring 2007
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

  1. Background: There are two ways that a system with an MMU can be configured to allow pointers to be passed from a program to a system call.

    First, if the operating system shares the address space with the running program, so pointers require no translation and the system may use them directly.

    Second, if the application is entitled to use of its entire address space, pointers passed to the system must be translated and for use, the object pointed to must be specially addressed.

    a) Explain why, with the first system, if the operating system forgets to properly check a pointer, the user might be able to attack the operating system. (1 point)

    The reason why the first operating system can be attacked because of a mistake done on the part of the system, is that the pointers that the user passes to execute the system calls are actual memory addresses i.e. no translation from virtual memory to physical memory needs to take place and therefore, if the system programmer forgets to include code to perform translation and checking, the system will still work correctly for correct user programs.

    b) Explain why the second system is inherently somewhat more resistant to error on the part of system programmers. (1 point)

    The second scheme is inherently safer than the above because it requires a translation of pointers from virtual to physical memory, and this translation can easily check the user's entitlement to use the pointer. If the system forgets call the routine to translate and check pointers, the code will not work for correct user programs. Therefore, the system code is unlikely to forget to check the pointers, unless foolish code has been written inside the system allowing it to translate pointers without checking their validity.

  2. Background: A thunk is a subroutine passed to another subroutine in order to facilitate passing a parameter. Thus, instead of passing the value i we pass a parameterless function geti() that returns the value of i, and instead of passing the address of i so that the system can change the value of i, we pass seti(v) that sets i to v. Microsoft makes extensive use of thunks in some of its system calls in order to avoid passing pointer parameters. The thunk, when called by the system, operates in the user's domain.

    Recall that when a trap occurs, all registers (including the PC) of the program that was running at the time are saved. The system may modify these registers arbitrarily before returning to the user program. Assume, for the purpose of this problem, that there is no sharing of memory address space between the user and the system domains.

    a) Explain, in some detail, how the system can call a thunk, once the system knows the address of the user's thunk. (1 point)

    The system needs to set user's saved PC so that it points to the thunk, and the system needs to save the previous value of the user's PC so that when the thunk returns, the system can restore it. In addition, the system needs to save a return address in the user's return address save area (user's stack or return address register) so that when the thunk tries to return, it will return to the system.

    Then the system does a return from trap, so that the domain changes from system to user. After this happens, the thunk executes as if it was called like any other normal user subroutine. Most of the points were taken off here if it was not mentioned that a return from trap has to take place.

    b) Propose a mechanism by which the thunk, once called, could return to the operating system. Keep in mind that the thunk itself is entirely normal user code. (Your answer to part b may solve this problem, in which case you need to make it clear that you understand how it does; if it doesn't, this is the point at which you can work on this issue.) (1 point)

    For the thunk to return to the system and not to the user program, then a trap needs to occur. This trap should be caused at the end of the execution of the thunk. Typically, either the saved return address that the thunk tries to return to will need to be invalid so that it causes a trap, or the return address will need to point to an instruction in the user's address space that is illegal and will cause a trap.

    The above 2 are really interesting situations because in the first one, a procedure call is done by a return from trap and in the second one, returning from a called routine is done using a trap. Most of the points were taken off here if it was not mentioned that a trap has to take place.

  3. A Problem: Given that the user passes buf, a pointer to a buffer, and len, the length of that buffer in bytes, write code for a system call using make_system_pointer and safe_bytes to set all of the bytes of the user's buffer to zero. (1 point)

    Here is the code. The one notable thing that had to be taken care of was make_system_pointer() should have been called only when safe_bytes() was called, a few points were taken off if this was not the case. Also, you had to make sure that every page that the buffer contains, you have write permissions for.

    void set_to_zero( char * buf, int len ) {
        while(len > 0){
            int safebytes;
            if (user_access_rights_allow( buf, write )){
                char * sysbuf = make_system_pointer( buf );
                int count = len;
                safebytes = safe_bytes(buf);
                if (len < safebytes) count = safebytes;
                while (count > 0) {
                      *sysbuf = 0;
                      sysbuf++;
                      buf ++;
                      count --;
                }
            }  else {
                report_user_protection_violation();
                break;
            }
            len = len - safebytes;
        }
    }