1

Hi I'm doing homework about operating system using Pintos. I was asked to find the faulting instruction in one test. The testing framework expected Pintos to output “do-nothing: exit(162)”. This is the standard message that Pintos prints when a process exits. However, Pintos did not output this message; instead, the do-nothing program crashed in userspace due to a memory access violation (a segmentation fault).

#include "tests/lib.h"

int
main (int argc UNUSED, char *argv[] UNUSED)
{
  return 162;
}

I looked into the result of this test,

FAIL
Test output failed to match any acceptable form.

Acceptable output:
  do-nothing: exit(162)
Differences in `diff -u' format:
- do-nothing: exit(162)
+ Page fault at 0xc0000008: rights violation error reading page in user context.
+ do-nothing: dying due to interrupt 0x0e (#PF Page-Fault Exception).
+ Interrupt 0x0e (#PF Page-Fault Exception) at eip=0x8048757
+  cr2=c0000008 error=00000005
+  eax=00000000 ebx=00000000 ecx=00000000 edx=00000000
+  esi=00000000 edi=00000000 esp=bfffffe4 ebp=00000000
+  cs=001b ds=0023 es=0023 ss=0023

The questions :

  1. What virtual address did the program try to access from userspace that caused it to crash? A: From the result file, I think it's 0xc000008
  2. What is the virtual address of the instruction that resulted in the crash? A: eip = 0x8048757, it's the virtual address of the instruction.
  3. To investigate, disassemble the do-nothing binary using objdump. What is the name of the function the program was in when it crashed? Copy the disassembled code for that function onto Gradescope, and identify the instruction at which the program crashed.

I have no idea about how to find the answer of 3. question, the output with "objdump -S do-nothing.o" is really simple :

Disassembly of section .text:

00000000 <main>:

int
main (int argc UNUSED, char *argv[] UNUSED)
{
  return 162;
}
   0:   b8 a2 00 00 00          mov    $0xa2,%eax
   5:   c3                      ret    

A:

void
_start (int argc, char *argv[])
{
 8048754:   83 ec 1c                sub    $0x1c,%esp
  exit (main (argc, argv));
 8048757:   8b 44 24 24             mov    0x24(%esp),%eax
 804875b:   89 44 24 04             mov    %eax,0x4(%esp)
 804875f:   8b 44 24 20             mov    0x20(%esp),%eax
 8048763:   89 04 24                mov    %eax,(%esp)
 8048766:   e8 35 f9 ff ff          call   80480a0 <main>
 804876b:   89 04 24                mov    %eax,(%esp)
 804876e:   e8 49 1b 00 00          call   804a2bc <exit>
  1. Find the C code for the function you identified above. For each instruction in the disassembled function in #3, explain in a few words why it’s necessary and/or what it’s trying to do.
#include <syscall.h>

int main (int, char *[]);
void _start (int argc, char *argv[]);

void
_start (int argc, char *argv[])
{
  exit (main (argc, argv));
}
  1. Why did the instruction you identified in #3 try to access memory at the virtual address you identified in #1? Don’t explain this in terms of the values of registers; we’re looking for a higher level explanation. A: I found the faulting instruction but I'm even more confused,
8048757:    8b 44 24 24             mov    0x24(%esp),%eax

**why would this instruction lead to segmentation fault? **

sub    $0x1c,%esp
mov    0x24(%esp),%eax

Firstly it allocated some stack space (0x1c), then move the argument argv at 0x24(%esp) [which was 0x8 before stack pointer changed] to %eax, why would this simple instruction lead to segmentation fault?

1 Answers1

2

I am not an expert in PintOS, but I can offer some insight. You are using OBJDUMP on an object file (.o). These are unlinked ELF object files without any VMA (Virtual Memory Address) starting point (aka Origin Point) and doesn't include any of the user mode runtime code in each PintOS program. Things like a C startup that sets up the user mode application and starts the code at main with the argc and argv parameters.

What you need to do is build the object file into a user mode program with the normal PintOS build processes. The userland executables have the same name as the .o file with the .o removed. do-nothing is the name of the executable and it appears these can be found in the directory pintos/src/userprog/build/tests/userprog/. Run OBJDUMP on this executable, search for address 0x8048757. You should be able to readily find the function name and all the code for it easily. With that information you should be able to answer the 3rd question. The answer you have for question 1 and 2 is correct.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • thanks a lot for your detailed answer. Could you please have a look at my updated question? I found the faulting instruction. It's "mov 0x24(%esp),%eax", I don't know why it would lead to a fault. – badtastetea Dec 02 '20 at 22:09
  • @badtastetea The exception err about the page fault says _rights violation error reading page in user context._ . This suggests that a user mode program attempted to access a page it didn't have privileges to read. Likely because the kernel addresses start at 0xc0000000 (which as probably mapped supervisor mode accessible). Why does that matter? The instruction `mov 0x24(%esp),%eax` has a memory operand and it is saying that the address 0x24+ESP is not accessible. If you look at the value in ESP when it failed it says it is 0xbfffffe4 when added to 0x24 is an address slightly above 0xc0000008. – Michael Petch Dec 02 '20 at 23:56
  • @badtastetea As for what that code is doing, it depends on how the process is passed the argc and argv on the stack. It seems to be copying the value of argc and argv from the stack and setting them up as the first parameters to `main`. The value 0x1c was probably chosen to be subtracted from the stack to build `main`s arguments and still maintain a 16 byte stack alignment. This is a guess since I don't know what the PintOS calling conventions stipulate, although the i386 System V ABI requires 16 byte alignment.hen claling an ABI compliant function (like `main`) – Michael Petch Dec 03 '20 at 00:02
  • I assume that `start` was called originally with a 16 byte aligned stack. The first instruction of `_start` would have a stack that is 4 bytes misaligned because the call to `_start` had its address pushed on the stack. So a value like 0x1c+4 =0x20 and 0x20 is evenly divisible by 16 (hex 0x10). Effectively all the code is doing is setting up the call to main with argc and argv as parameters and keeping the stack properly aligned. – Michael Petch Dec 03 '20 at 00:08
  • The fault though suggests that whoever called `_start` didn't actually push argc and argv as parameters to `_start` and when `_start` tried to copy them to the stack for function `main` - they weren't there and the resulting stack pointer (ESP) + 0x24 failed. – Michael Petch Dec 03 '20 at 00:11
  • Section 3.5.1 of this document explains the calling convention for the startup code: https://web.stanford.edu/class/cs140/projects/pintos/pintos_3.html – Michael Petch Dec 03 '20 at 00:14