Advertisement
Guest User

Untitled

a guest
Nov 30th, 2015
70
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.56 KB | None | 0 0
  1. From: goo.gl/ElW6iF
  2. x86 gcc 4.4.7
  3. compiler options: -O
  4.  
  5. /* Do not use the end_ptr programming idiom in C
  6. A C-and-assembly blog post */
  7.  
  8. #include <string.h>
  9. #include <stdlib.h>
  10. #include <stdio.h>
  11.  
  12. /*
  13. Consider a simple function for comparing pointers:
  14. */
  15. const char * ptr_lower(char *p, long offset, char *q) {
  16. if (p + offset < q) return "below"; else return "above";
  17. }
  18.  
  19. /*
  20. The translation of “+” in that function to x86 uses the addq instruction.
  21. This instruction adds two 64-bit operands. It doesn't matter whether
  22. they should be interpreted as signed or unsigned quantities. The
  23. 64-bit result is the same.
  24.  
  25.  
  26. The source-level “<” comparison translates to a cmpq instruction.
  27. This instruction sets a number of processor “flags” according to
  28. the values of its two operands.
  29.  
  30. Two instructions later, the instruction cmovb tests whether the flags
  31. reveal that the first operand (translation of “p + offset”) is less
  32. than the second operand (translation of “q”) when interpreted as
  33. unsigned 64-bit integers, and makes the function return "below" if
  34. it's the case, "above" if it's not.
  35. */
  36.  
  37. /*
  38. What does this mean for a program that compare a pointer obtained by
  39. adding an attacker-controlled offset to another pointer?
  40. */
  41.  
  42. size_t get_size(void);
  43. void read_contents(char *, size_t);
  44. void treat_sub_component(char *, size_t);
  45.  
  46. int main(int c, char *v[]) {
  47. size_t size = get_size();
  48. char *buffer = (char*)malloc(size); // this is C++
  49. if (!buffer) abort();
  50.  
  51. char *end_buffer = buffer + size; // one past end of buffer
  52. read_contents(buffer, size);
  53.  
  54. /*
  55. The above is the beginning of a simplified hand-written parser
  56. for some binary format. The format may involve sub-components, that
  57. declare their own size:
  58. */
  59. size_t sub_component_size;
  60. sub_component_size = buffer[0] + (1UL<<8) * buffer[1] + (1UL<<16) * (size_t)buffer[2];
  61. buffer += 3;
  62. size -= 3;
  63.  
  64. /*
  65. Of course an attacker may try to trick us into reading out of
  66. the allocated buffer, by declaring a larger sub_component_size than
  67. the buffer itself. But we weren't born yesterday, we'll check
  68. that this isn't the case:
  69. */
  70. const char *comparison = ptr_lower(buffer, sub_component_size, end_buffer);
  71. puts(comparison);
  72.  
  73. /*
  74. There is still a problem!
  75.  
  76. The problem is that the assembly code for function ptr_lower
  77. only works for valid pointers to the same array of chars.
  78.  
  79. By computing buffer + sub_component_size for a value of
  80. sub_component_size that the attacker may choose larger than size,
  81. the function ptr_lower invokes undefined behavior.
  82.  
  83. The compiler does not have to produce code and memory layout
  84. that makes the comparison return "above" in this case. Indeed,
  85. if buffer is allocated as the very top of the address space,
  86. the addq instruction will produce a 64-bit value that, interpreter
  87. as an unsigned quantity, is very small, smaller than buffer.
  88. */
  89.  
  90. if (comparison[0] == 'b') {
  91. /* here something bad can happen, if we are unlucky
  92. (or if the attacker is persistent!) */
  93. treat_sub_component(buffer, sub_component_size);
  94. }
  95. /* ... */
  96. }
  97.  
  98. *****************************************************************************
  99.  
  100. asm:
  101.  
  102. .LC0:
  103. .string "above"
  104. .LC1:
  105. .string "below"
  106. ptr_lower(char*, long, char*):
  107. addq %rsi, %rdi
  108. cmpq %rdx, %rdi
  109. movl $.LC0, %eax
  110. movl $.LC1, %edx
  111. cmovb %rdx, %rax
  112. ret
  113. main:
  114. movq %rbx, -32(%rsp)
  115. movq %rbp, -24(%rsp)
  116. movq %r12, -16(%rsp)
  117. movq %r13, -8(%rsp)
  118. subq $40, %rsp
  119. call get_size()
  120. movq %rax, %rbp
  121. movq %rax, %rdi
  122. call malloc
  123. movq %rax, %rbx
  124. testq %rax, %rax
  125. jne .L6
  126. call abort
  127. .L6:
  128. movq %rbp, %rsi
  129. movq %rax, %rdi
  130. call read_contents(char*, unsigned long)
  131. movsbq 2(%rbx),%r13
  132. salq $8, %r13
  133. movsbq 1(%rbx),%rax
  134. addq %rax, %r13
  135. salq $8, %r13
  136. movsbq (%rbx),%rax
  137. addq %rax, %r13
  138. leaq 3(%rbx), %r12
  139. leaq (%rbx,%rbp), %rdx
  140. movq %r13, %rsi
  141. movq %r12, %rdi
  142. call ptr_lower(char*, long, char*)
  143. movq %rax, %rbx
  144. movq %rax, %rdi
  145. call puts
  146. cmpb $98, (%rbx)
  147. jne .L7
  148. movq %r13, %rsi
  149. movq %r12, %rdi
  150. call treat_sub_component(char*, unsigned long)
  151. .L7:
  152. movl $0, %eax
  153. movq 8(%rsp), %rbx
  154. movq 16(%rsp), %rbp
  155. movq 24(%rsp), %r12
  156. movq 32(%rsp), %r13
  157. addq $40, %rsp
  158. ret
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement