Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- From: goo.gl/ElW6iF
- x86 gcc 4.4.7
- compiler options: -O
- /* Do not use the end_ptr programming idiom in C
- A C-and-assembly blog post */
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- /*
- Consider a simple function for comparing pointers:
- */
- const char * ptr_lower(char *p, long offset, char *q) {
- if (p + offset < q) return "below"; else return "above";
- }
- /*
- The translation of “+” in that function to x86 uses the addq instruction.
- This instruction adds two 64-bit operands. It doesn't matter whether
- they should be interpreted as signed or unsigned quantities. The
- 64-bit result is the same.
- The source-level “<” comparison translates to a cmpq instruction.
- This instruction sets a number of processor “flags” according to
- the values of its two operands.
- Two instructions later, the instruction cmovb tests whether the flags
- reveal that the first operand (translation of “p + offset”) is less
- than the second operand (translation of “q”) when interpreted as
- unsigned 64-bit integers, and makes the function return "below" if
- it's the case, "above" if it's not.
- */
- /*
- What does this mean for a program that compare a pointer obtained by
- adding an attacker-controlled offset to another pointer?
- */
- size_t get_size(void);
- void read_contents(char *, size_t);
- void treat_sub_component(char *, size_t);
- int main(int c, char *v[]) {
- size_t size = get_size();
- char *buffer = (char*)malloc(size); // this is C++
- if (!buffer) abort();
- char *end_buffer = buffer + size; // one past end of buffer
- read_contents(buffer, size);
- /*
- The above is the beginning of a simplified hand-written parser
- for some binary format. The format may involve sub-components, that
- declare their own size:
- */
- size_t sub_component_size;
- sub_component_size = buffer[0] + (1UL<<8) * buffer[1] + (1UL<<16) * (size_t)buffer[2];
- buffer += 3;
- size -= 3;
- /*
- Of course an attacker may try to trick us into reading out of
- the allocated buffer, by declaring a larger sub_component_size than
- the buffer itself. But we weren't born yesterday, we'll check
- that this isn't the case:
- */
- const char *comparison = ptr_lower(buffer, sub_component_size, end_buffer);
- puts(comparison);
- /*
- There is still a problem!
- The problem is that the assembly code for function ptr_lower
- only works for valid pointers to the same array of chars.
- By computing buffer + sub_component_size for a value of
- sub_component_size that the attacker may choose larger than size,
- the function ptr_lower invokes undefined behavior.
- The compiler does not have to produce code and memory layout
- that makes the comparison return "above" in this case. Indeed,
- if buffer is allocated as the very top of the address space,
- the addq instruction will produce a 64-bit value that, interpreter
- as an unsigned quantity, is very small, smaller than buffer.
- */
- if (comparison[0] == 'b') {
- /* here something bad can happen, if we are unlucky
- (or if the attacker is persistent!) */
- treat_sub_component(buffer, sub_component_size);
- }
- /* ... */
- }
- *****************************************************************************
- asm:
- .LC0:
- .string "above"
- .LC1:
- .string "below"
- ptr_lower(char*, long, char*):
- addq %rsi, %rdi
- cmpq %rdx, %rdi
- movl $.LC0, %eax
- movl $.LC1, %edx
- cmovb %rdx, %rax
- ret
- main:
- movq %rbx, -32(%rsp)
- movq %rbp, -24(%rsp)
- movq %r12, -16(%rsp)
- movq %r13, -8(%rsp)
- subq $40, %rsp
- call get_size()
- movq %rax, %rbp
- movq %rax, %rdi
- call malloc
- movq %rax, %rbx
- testq %rax, %rax
- jne .L6
- call abort
- .L6:
- movq %rbp, %rsi
- movq %rax, %rdi
- call read_contents(char*, unsigned long)
- movsbq 2(%rbx),%r13
- salq $8, %r13
- movsbq 1(%rbx),%rax
- addq %rax, %r13
- salq $8, %r13
- movsbq (%rbx),%rax
- addq %rax, %r13
- leaq 3(%rbx), %r12
- leaq (%rbx,%rbp), %rdx
- movq %r13, %rsi
- movq %r12, %rdi
- call ptr_lower(char*, long, char*)
- movq %rax, %rbx
- movq %rax, %rdi
- call puts
- cmpb $98, (%rbx)
- jne .L7
- movq %r13, %rsi
- movq %r12, %rdi
- call treat_sub_component(char*, unsigned long)
- .L7:
- movl $0, %eax
- movq 8(%rsp), %rbx
- movq 16(%rsp), %rbp
- movq 24(%rsp), %r12
- movq 32(%rsp), %r13
- addq $40, %rsp
- ret
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement