Assume the following code in SM213 Assembly:
_1: deca r5
_2: deca r5
_3: ld $22, r0
_4: st r0, (r5)
_5: ld $25, r0
_6: st r0, 4(r5)
_7: gpc $6, r6
_8: j foo
_9: inca r5
_10: inca r5
_11: halt
_12: foo: deca r5
_13: deca r5
_14: st r6, 4(r5)
_15: ld 8(r5), r2
_16: ld $0x3000, r0
_17: ld (r0), r1
_18: ld 12(r1), r2
_19: ld 12(r5), r3
_20: st r3, 0(r5)
_21: ld 4(r1), r2
_22: st r2, 8(r1)
_23: ld 4(r5), r6
_24: inca r5
_25: inca r5
_26: j(r6)
Which statements below best describes the following lines of the assembly code above?
Options:
_20: st r3, 0(r5): It reads from or writes to a local variable._14: st r6, 4(r5): It saves or restores the return address to or from the stack._19: ld 12(r5), r3: It reads from or writes to an argument._17: ld (r0), r1: It reads from or writes to a global variable.Match each line with the most accurate description of its role
foo: ld $-8, r1
add r1, r5 # A
st r6, 4(r5)
ld 8(r5), r2
ld 12(r5), r3
ld $-8, r1
add r1, r5 # B
st r2, (r5)
st r3, 4(r5)
gpc $6, r6
j p # C
ld $8, r1
add r1, r5 # D
st r0, (r5)
ld 4(r5), r6
ld $8, r1
add r1, r5 # E
j (r6)
Options:
Consider the following C code.
int w;
void func(int d) {
int n;
// some other code
w = d + n; // <== Translate this line only
bar();
// some more code
}
Convert the highlighted line above to an appropriate SM213 equivalent code by dragging appropriate lines of code from the left panel to the right panel. You should convert only the highlighted line. Do not assume that any values have been loaded into registers, other than the stack pointer (r5) and the return address (r6). Assume that the order of values in the stack is the usual order used in class, i.e., that local variables are found above the return address, which is found above arguments.
ld $w, r0
ld (r5), r1 # n
ld 8(r5), r2 # d
add r1, r2
st r2, (r0)
Consider the following C code.
int d;
int bar(int);
int bat(void) {
// some other code
d = bar(56); // <== Translate this line only
// some more code
}
Convert the highlighted line above to an appropriate SM213 equivalent code by dragging appropriate lines of code from the left panel to the right panel. You should convert only the highlighted line. Do not assume that any values have been loaded into registers, other than the stack pointer (r5) and the return address (r6).
deca r5
ld $56, r0
st r0, (r5)
gpc $6, r6
j bar
inca r5
ld $d, r1
st r0, (r1)
Assume the following C code is given to you. Assume that both dynamic memory allocation and stack initialization have already been done. Start your code with the label list_sum.
Keep in mind that pointers are 4 bytes long in SM213.
// Function Definition
int list_sum(int* list) {
if (*list) {
return *list + list_sum(list + 1);
}
return 0;
}
Translate the function definition into SM213 assembly.
list_sum:
# -=- callee prologue -=-
deca r5
st r6, (r5) # store return address after args
# -=- callee prologue -=-
ld 4(r5), r1 # list
ld (r1), r2 # *list
beq r2, list_null # *list == nullptr
br list_not_null
list_null:
ld $0, r0
br return
list_not_null:
inca r1 # list + 1
# -=- prologue epilogue -=-
deca r5
st r1, (r5)
gpc $6, r6
# -=- prologue epilogue -=-
# -=- call -=-
j list_sum
# -=- call -=-
# -=- prologue epilogue -=-
inca r5
# -=- prologue epilogue -=-
ld 4(r5), r1
ld (r1), r2
add r2, r0
br return
return:
# -=- callee epilogue -=-
ld (r5), r6
inca r5
j (r6)
# -=- callee epilogue -=-
Assume the following C code is given to you. Assume that both dynamic memory allocation and stack initialization have already been done.
Keep in mind that pointers are 4 bytes long in SM213.
// Declarations
struct Node {
int val;
struct Node* left;
struct Node* right;
};
int a;
struct Node* root;
// Function Definition
int tree_sum(struct Node* node) {
if (node == NULL) {
return 0;
}
return ((node->val & 1) ? 0 : node->val) + tree_sum(node->left) + tree_sum(node->right);
}
// Function Call
a = tree_sum(root);
Translate the function definition and the call to it into SM213 assembly.
ld $root, r1 # r1 = address of root
ld (r1), r2 # r2 = value of root (the pointer)
deca r5 # allocate space for argument
st r2, (r5) # push root as argument
gpc $6, r6 # save return address
j tree_sum # call tree_sum(root)
inca r5 # pop argument
ld $a, r1 # r1 = address of a
st r0, (r1) # a = return value
halt # stop program
# Function Definition
tree_sum:
# -=- callee prologue -=-
deca r5 # allocate space for saved r6
st r6, (r5) # save return address
deca r5 # allocate space for local variables
# -=- callee prologue -=-
ld 8(r5), r1 # r1 = node (parameter at offset 8)
beq r1, node_is_null # if (node == NULL)
br node_is_not_null
node_is_null:
ld $0, r0 # return 0
br end_if
node_is_not_null:
ld 8(r5), r1 # r1 = node
ld (r1), r2 # r2 = node->val
ld $1, r3 # r3 = 1
and r2, r3 # r3 = node->val & 1
beq r3, val_is_even # if ((node->val & 1) == 0)
br val_is_odd
val_is_even:
ld 8(r5), r1 # r1 = node
ld (r1), r2 # r2 = node->val
st r2, (r5) # store node->val in local variable
br end_ternary
val_is_odd:
ld $0, r2 # r2 = 0
st r2, (r5) # store 0 in local variable
end_ternary:
# Call tree_sum(node->left)
ld 8(r5), r1 # r1 = node
ld 4(r1), r2 # r2 = node->left (offset 4 for pointer)
deca r5 # allocate space for argument
st r2, (r5) # push node->left
gpc $6, r6 # save return address
j tree_sum # call tree_sum
inca r5 # pop argument
ld (r5), r2 # r2 = accumulated value
add r0, r2 # r2 += return value from tree_sum(node->left)
st r2, (r5) # store back
# Call tree_sum(node->right)
ld 8(r5), r1 # r1 = node
ld 8(r1), r2 # r2 = node->right (offset 8 for pointer)
deca r5 # allocate space for argument
st r2, (r5) # push node->right
gpc $6, r6 # save return address
j tree_sum # call tree_sum
inca r5 # pop argument
ld (r5), r2 # r2 = accumulated value
add r0, r2 # r2 += return value from tree_sum(node->right)
mov r2, r0 # move final result to r0
end_if:
# -=- callee epilogue -=-
inca r5 # deallocate local variables
ld (r5), r6 # restore return address
inca r5 # deallocate saved r6
j (r6) # return
# -=- callee epilogue -=-
When converting C code into Assembly, the compiler often has the option to optimize the code and use registers instead of stack memory locations for some local variables and values. It also has the ability to avoid storing the value of register r6 into the stack in some situations.
Based on the conventions using in class for the value of r6, when would a compiler be compelled to store the value of r6 into memory (i.e., when is the optimization of using registers only not possible for register r6)?
Let us assume that a new architecture is deployed in which the stack grows down instead of up. This means that allocating an activation frame is accomplished by ADDING to the stack pointer. Let us also assume that in each activation frame, the return address is located before arguments and local variables (i.e., the stack address where the return address is stored is lower than the stack address for arguments and local variables).
Does this new architecture change the circumstances under which the stack-smash, buffer-overflow attack is possible? If so, how?
Consider the copy() routine below, which is vulnerable to stack-smash attacks, and assume you have a suitable attack string for this function.
void copy() {
int i = 0;
int dst[100];
while (src[i] != 0) {
dst[i] = src[i];
i++;
}
dst[i] = 0;
}
Suppose that the function is now changed, as shown below, to add an extra local variable j to the copy routine.
void copy() {
int i = 0;
int dst[100];
while (src[i] != 0) {
dst[i] = src[i];
i++;
}
int j = 0xF0F0F0F0;
dst[i] = 0;
}
Suppose that local variables are stored as discussed in class: in the order in which they are declared, with the first local variable stored at the start of the function's activation frame (i.e., 0(r5)), with following variables in increasing offset order; and that the return address is stored after the local variables, but before the arguments. Assume there is no local variable optimization, i.e., all local variables are stored in memory in the stack, as is the return address.
Which of the following statements is true in regards to your existing attack string? Assume that local variables are stored in the stack in the same order as they are declared in the C code, with the first variable stored on top of (with a lower numeric address than) the second variable, and so on.