college/Fall-2024/CS-3843/Assignments/4/Assignment.typ

224 lines
9.9 KiB
Plaintext
Raw Normal View History

2024-10-20 15:48:32 -05:00
#import "@preview/tablex:0.0.4": tablex, rowspanx, colspanx, hlinex, vlinex
#show link: set text(blue)
#set text(font: "Calibri")
#show raw: set text(font: "Fira Code")
// Thank you to https://github.com/typst/typst/issues/344#issuecomment-2079135253
#let codeblock(
body,
fill: luma(250),
stroke-color: luma(150),
line-nr-color: blue,
caption: none,
) = {
show raw.where(block: true): it => {
block(
fill: fill,
stroke: stroke-color + .1em,
inset: 0.3em,
radius: 0.3em,
grid(
columns: 2,
align: left + top,
column-gutter: 0.2em,
stroke: (x, y) => if x == 0 {
(right: (paint: stroke-color))
},
inset: 0.25em,
..it
.lines
.map(line => (text(line-nr-color, str(line.number)), line.body))
.flatten()
),
)
}
body
}
#set page(margin: (x: .25in, y: .25in))
#let solve(solution) = [
#let solution = align(
center,
block(
inset: 5pt,
stroke: blue + .3pt,
fill: rgb(0, 149, 255, 15%),
radius: 4pt,
)[#align(left)[#solution]],
)
#solution
]
#let note(content) = [
#align(
center,
block(
inset: 5pt,
stroke: luma(20%) + .3pt,
fill: luma(95%),
radius: 4pt,
)[#align(left)[#content]],
)
]
#align(center)[
= CS 3843 Computer Organization
HW 4\
#underline[Price Hiller] *|* #underline[zfp106]
]
#line(length: 100%, stroke: .25pt)
+ Write a function with the following prototype [in C]:
#solve[
```c
/* Determine whether arguments can be added without overflow */
int uadd_ok(unsigned x, unsigned y);
```
This function should return 1 if arguments x and y can be added without causing overflow.
```c
#include <limits.h>
#define WILL_OVERFLOW 0
int uadd_ok(unsigned x, unsigned y) {
if (x > 0 && y > UINT_MAX - x) {
return WILL_OVERFLOW;
}
return !WILL_OVERFLOW;
}
```
#underline[Test Results]
- `UINT_MAX + 1 = OVERFLOW (0)`
- `UINT_MAX + 0 = OK (1)`
]
+ Write a function with the following prototype in C:
```c
/* Determine whether arguments can be added without overflow */
int tadd_ok(int x, int y)
```
This function should return 1 if arguments x and y can be added without causing overflow.
#solve[
```c
#include <limits.h>
#define WILL_OVERFLOW 0
int tadd_ok(int x, int y) {
if ((x > 0 && y > INT_MAX - x) || (x < 0 && y < INT_MIN - x)) {
return WILL_OVERFLOW;
}
return !WILL_OVERFLOW;
}
```
#underline[Test Results]
- `INT_MAX + 1 = OVERFLOW (0)`
- `INT_MAX + 0 = OK (1)`
- `INT_MIN + -1 = UNDERFLOW (0)`
- `INT_MIN + 0 = OK (0)`
]
Note: You need to include the function as part of the whole C program for testing your function. While you write your function, for testing, try out general test cases. Please include your test results and the function as your answer. You don't need to include the whole program. You need to submit a pdf file, not a .c file.
+ Read about this security vulnerability and share your thoughts in one short paragraph.
#block(
inset: 5pt,
stroke: green + .3pt,
fill: rgb(0, 200, 20, 15%),
radius: 4pt,
)[
#align(left)[
In 2002, programmers involved in the FreeBSD open-source operating systems project realized that their implementation of the `getpeername` library function had a security vulnerability. A simplified version of their code went something like this:
#codeblock[
```c
/*
* Illustration of code vulnerability similar to that found in
* FreeBSD's implementation of getpeername()
*/
/* Declaration of library function memcpy */
void *memcpy(void *dest, void *src, size_t n);
/* Kernel memory region holding user-accessible data*/
#define KSIZE 1024
char kbuf[KSIZE];
/* Copy at most maxlen bytes from kernel region to user buffer */
int copy_from_kernel(void *user_dest, int maxlen) {
/* Byte count len is minimum of buffer size and maxlen */
int len = KSIZE < maxlen ? KSIZE : maxlen;
memcpy(user_dest, kbuf, len);
return len;
}
```
\<remaining explanation removed for brevity\>
]
]
]
#solve[
As in all of the security vulnerabilities shown in this homework, they all result from a failure of variance around data types. This bug arose because a clear typing invariant wasn't properly defined, that being that non-negative values are not allowed. More modern languages like Rust can and _do_ validate types at compile time to ensure bugs like these cannot occur within "safe" code. The solution the FreeBSD's folk came up with was to better define the variant as being of `size_t` meaning non-negative values could no longer be passed. These types of bugs are incredibly common in C/C++ code bases unfortunately. A hardened memory allocator like #link("https://llvm.org/docs/ScudoHardenedAllocator.html")[Scudo] also would have reduced the severity of this bug had Scudo existed back in 2002.
]
+ Read about this security vulnerability and share your thoughts in one short paragraph.
#block(
inset: 5pt,
stroke: green + .3pt,
fill: rgb(0, 200, 20, 15%),
radius: 4pt,
)[#align(left)[
In 2002, it was discovered that code supplied by Sun Microsystems to implement the XDR library, a widely used facility for sharing data structures between programs, had a security vulnerability arising from the fact that multiplication can overflow without any notice being given to the program.
Code similar to that containing the vulnerability is shown below:
#codeblock[
```c
/* Illustration of vulnerability similar to that found in
* Sun's XDR library.
*/
void* copy_elements(void *ele_src[], int ele_cnt, size_t ele_size) {
/*
* Allocate buffer for ele_cnt objects, each of ele_size bytes
* and copy from locations designated by ele_src
*/
void *result = malloc(ele_cnt * ele_size);
if (result == NULL)
/* malloc failed */
return NULL;
void *next = result;
int i;
for (i = 0; i < ele_cnt; i++) {
/* Copy object i to destination */
memcpy(next, ele_src[i], ele_size);
/* Move pointer to next memory region */
next += ele_size;
}
return result;
}
```
\<remaining explanation removed for brevity\>
]
]
]
#solve[
This is a more subtle bug. A variance check _still_ could have caught this with better type checking (in fact some modern flags in the LLVM compiler can catch multiplication errors), but the difficulty in creating those bounds for this are significantly more difficult. In all honesty, during my first read through of the code I missed the bug buried there even with being told that there was a bug in the first place. One approach without variant checking that would have avoided the bug would have been to call `malloc` during the loop for each object. There absolutely would've been a performance penalty, but it would have avoided the vulnerability here. A somewhat better solution though, is to do some simple bound checks of course. For example #link("https://github.com/rofl0r/musl/blob/2c124e13bd7941fe0b885eecdc5de6f09aacf06a/src/malloc/calloc.c#L35-L37")[`calloc()` nowadays in the `musl` libc implementation does do multiplication bounding through a simple check].
]
+ Read about this incident of high cost floating-point overflow. Write a short paragraph sharing your thoughts.
#block(
inset: 5pt,
stroke: green + .3pt,
fill: rgb(0, 200, 20, 15%),
radius: 4pt,
)[
#align(left)[
Converting large floating-pointer numbers to integers is a common source of programming errors. Such an error had disastrous consequences for the maiden voyage of the Ariane 5 rocket, on June 4, 1996. Just 37 seconds after liftoff, the rocket veered off its flight path, broke up, and exploded. Communication satellites valued at \$500 million were on board the rocket.
A later investigation [73, 33] showed that the computer controlling the inertial navigation system had sent invalid data to the computer controlling the engine nozzles. Instead of sending flight control information, it had sent a diagnostic bit pattern indicating that an overflow had occurred during the conversion of a 64-bit floating-point number to a 16-bit signed integer.
The value that overflowed measured the horizontal velocity of the rocket, which would be more than five times higher than that achieved by the earlier Ariane 4 rocket. In the design of the Ariane 4 software, they had carefully analyzed the numeric values and determined that the horizontal velocity would never overflow a 16-bit number. Unfortunately, they simply reused this part of the software in the Ariane 5 without checking the assumptions on which it had been based.
]
]
#solve[Firstly, a good criticism to level is the casting to a less precise data type with smaller bounds. I'm not quite sure why there was a decision made to cast down into a 16-bit number instead of working directly with the initial 64-bit number. The only guess I can make is limited memory in the stack, though that seems to be a poor guess. Again though, this is a variance bounding failure! An incredible number of C/C++ errors result from that. Invariants around the horizontal velocity were not clearly defined in the type system so the compiler couldn't do any variance checking and thus the code was allowed to compile with a serious bug in it. Defining clear variants and bounds in type systems would largely avoid the pitfalls here. For this case, outside of variance checking which was less mature in compilers during this incident, some basic bounds checking for the truncation of values would have caught this before blowing up a \$1 billion plus rocket.]