CMSC 421: Principles of Operating Systems

Homework 4: Canary of Your Overall Grade

This homework is due on Thursday, April 6, at 11:59:59 PM (Eastern daylight time). You must use submit to turn in your homework like so: submit cs421_jtang hw4 hw4.c

Your program must be named hw4.c, and it will be compiled on Ubuntu 16.04 as follows:

  gcc ‐‐std=c99 ‐Wall ‐O2 ‐o hw4 hw4.c hw4_test.c ‐pthread
(Note the above is dash, dash, "std=c99", and the other flags likewise are preceded by dashes.) There must not be any compilation warnings in your submission; warnings will result in grading penalties. In addition, your code must be properly indented and have a file header comment, as described on the coding conventions page.

In this homework, you are writing a memory allocator from scratch. You will implement a first-fit allocation algorithm. Due to recent cyber attacks, you will add canaries to help detect buffer overflows. As proof that your code works, your program will run instructor-provided unit tests that will exercise all functions.

Part 1: Memory Region

Your program will simulate a machine's memory system. The machine has 16 page frames, where each frame holds 64 bytes, for 1024 bytes total. Declare this "memory" as a global array. When your program starts, initialize its contents to 0. You will also need to design your own memory allocation table.

Implement the following function with this given signature. Note that the comments are formatted using Doxygen codes.

/**
 * Write to standard input information about the current memory
 * allocations.
 *
 * Display to standard output the following:
 * - Memory contents, one frame per line, 16 lines total. Display the
 *   actual bytes stored in memory. If the byte is unprintable (ASCII
 *   value less than 32 or greater than 126), then display a dot
 *   instead.
 * - Current memory allocation table, one line of 16 columns. For each
 *   column, display a 'f' if the frame is free, 'R' if reserved. If
 *   the frame is the beginning of a reserved memory block, display
 *   the four hexadecimal digit canary. Otherwise, display dashes
 *   instead of the canary.
 */
void my_malloc_stats(void);

Part 2: Simple Memory Allocation

Next, implement a first-fit memory allocator. You must implement these functions with these given signatures:

/**
 * Allocate and return a contiguous memory block that is within the
 * memory region.
 *
 * The size of the returned block will be at least @a size bytes,
 * rounded up to the next 64-byte increment.
 *
 * @param size Number of bytes to allocate. If @c 0, your code may do
 * whatever it wants; my_malloc() of @c 0 is "implementation defined",
 * meaning it is up to you if you want to return @c NULL, segfault,
 * whatever.
 *
 * @return Pointer to allocated memory, or @c NULL if no space could
 * be found. If out of memory, set errno to @c ENOMEM.
 */
void *my_malloc(size_t size);

/**
 * Deallocate a memory region that was returned by my_malloc() or
 * my_asprintf().
 *
 * If @a ptr is not a pointer returned by my_malloc() or
 * my_asprintf(), then raise a SIGSEGV signal to the calling
 * thread. Likewise, calling my_free() on a previously freed region
 * results in a SIGSEGV.
 *
 * If the canary assocatied with @a ptr changed, then raise a SIGUSR1
 * signal to the calling process. The memory region will still be
 * deallocated.
 *
 * @param ptr Pointer to memory region to free. If @c NULL, do
 * nothing.
 */
void my_free(void *ptr);

For my_malloc(), you are to implement a first-fit allocation strategy. Because each page frame is 64 bytes, my_malloc() must round up to the next 64-byte boundary when reserving space, and the returned address must be frame-aligned. Your function allocates using the space reserved in step 1 above.

For my_free(), your program deallocates space that was returned by my_malloc() or my_asprintf() (see below). Thus, if my_malloc() previously allocated 5 frames and returned the address to the first frame, calling my_free() will deallocate all 5 frames (not just the first).

Part 3: Advanced Memory Allocation

Next, implement this function with this given signature:

/**
 * Format a string into a newly allocated memory block.
 *
 * Calculate the length that a formatted string (as per snprintf())
 * would be, then allocate contiguous memory block(s) of at least that
 * length. Then output the resulting string, including terminating
 * null character ('\0') to that memory. The starting address of the
 * memory block is written to the pointer pointed to by @a strp. The
 * pointer can be freed via my_free() afterwards.
 *
 * @param strp Pointer to where to write pointer to.
 *
 * @param fmt Format specification, as per snprintf().
 *
 * @return Number of bytes allocated (excluding terminating null
 * byte), if memory allocation succeeds. If out of memory, @a strp is
 * @c NULL, or some other error occurs, return -1.
 */
int my_asprintf(char **strp, const char *fmt, ...);
  

Part 4: Canaries

The last thing to implement is a canary. Canaries are special values appended to the end of memory allocation blocks, used to detect buffer overflows. Implement this feature as follows:

  1. As part of your program startup, call srand(), passing the PID as the argument.
  2. For every allocation (my_malloc() or my_asprintf()), silently reserve an extra two bytes to hold the canary. This may require allocating an additional frame for the canary.
  3. As part of allocation, call rand(). Store the bottom 16 bits in the canary region. Also store those two bytes in your memory allocation table.
  4. When my_free() is called, also check the canary. If the passed in pointer is valid, but if the canary differs from the value in the memory allocation table for that memory block, send SIGUSR1 to the process. Deallocate the pointer even if the canary changed.

Part 5: Testing

Within your Ubuntu VM, use the wget command to fetch the unit test code file http://www.csee.umbc.edu/~jtang/cs421.s16/homework/hw4_test.c. This file declares a function with the following signature:

/**
 * Unit test of your memory allocator implementation. This will
 * allocate and free memory regions.
 */
extern void hw4_test(void);
In your main() function, after you have initialized your memory, call hw4_test().

The grader will use a different unit test code file during grading. You may not make any assumptions about what hw4_test() will do, other than that its code will compile and that it will not cause compilation warnings. You should consider writing your own hw4_test.c, as that the supplied one does not thoroughly test all edge cases. You may safely assume that the grader will examing attempt all edge conditions.

Note how this code implements a basic testing framework, that prints what it is about to test, if the test passes or fails, and the sum of passed and failed tests. Read over hw4_test.c; for the projects you will need to write your own unit tests. If you continue your career in software engineering, be familiar with other unit testing frameworks.

Part 6: Documentation

In your code, add a comment block describing how you track which frames are allocated and how large each memory block is. Specifically, describe how your code would handle these two scenarios:

  1. How would a call to my_free() would know to deallocate 5 frames if the user had previously called my_malloc() for 300 bytes?
  2. How does my_free() know it needs to raise the SEGFAULT signal when the passed in pointer points into the middle of a memory block?

Sample Output

Here is a sample output from running the program using the above hw4_test.c. As that canary values are random, your output may differ.

Test 1: Display initialized memory
Memory contents:
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
Memory allocation table:
    f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----
Test 2: Simple allocations
Test 3: Simple freeing
Memory contents:
  CCCCCCCCAAAAAAAAAAAAAAAAAAAAAA.0.............{..................
  BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
  BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
Memory allocation table:
    f:----  R:1df9  R:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----
Test 4: Out of memory condition
Test 5: Double-free
Caught signal 11: Segmentation fault: 11!
Memory contents:
  CCCCCCCCAAAAAAAAAAAAAAAAAAAAAA.0.............{..................
  BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
  BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
Memory allocation table:
    f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----
Test 6: Complex Allocation
Test 7: Corrupted canary
Caught signal 30: User defined signal 1: 30!
Memory contents:
  CMSC 421 - Homework 4...AAAAAA.0.............{..................
  BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
  BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.................................
  ................................................................
  ................................................................
  9...............................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
Memory allocation table:
    R:06ba  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----  f:----
9 tests passed, 0 tests failed.

Other Hints and Notes

Extra Credit

You may earn an additional 5% on this assignment by making your functions thread-safe. If a thread is executing any of the above functions, then another thread calling any of the functions must block. You can test your implementation by spawning multiple threads and them simultaneously execute my_malloc() and my_free().

Note that in Part 4, the call to rand() itself is not thread-safe. Change your code to use rand_r() instead.

If you choose to perform this extra credit, put a comment at the top of your file, alerting the grader.