owned this note
owned this note
Published
Linked with GitHub
---
tag:進階電腦系統理論與實作 sysprog2018
---
# review
contributed by <`plusline`>
### week 2 --- test 1
存在的問題
- C99/C11 6.4.2第二點
If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to reaches the end of its lifetime.
修正機制
- C99/C11 6.4.2第三點
用 static 將生命週期延長到執行程式的整個時間。
#### CVE-2018-4882
Vulnerability Category:
- Security Mitigation Bypass
- Heap Overflow
- Use-after-free
- Out-of-bounds write
- Out-of-bounds read
- Memory corruption
#### Out-of-bounds Read
給予 array 的 index 需要嚴格界定範圍,不要只記得界定下限,也要檢查負數。[CWE-125: Out-of-bounds Read](http://cwe.mitre.org/data/definitions/125.html)
測試:
```c
#include <stdio.h>
int main(){
int a[5];
for(int i=0;i<5;i++)a[i]=i;
for(int i=-5;i<10;i++)printf("No.%d %d\n",i,a[i]);
return 0;
}
```
```
No.-5 21899
No.-4 749111712
No.-3 32583
No.-2 5
No.-1 -1
No.0 0
No.1 1
No.2 2
No.3 3
No.4 4
No.5 32766
No.6 2126830848
No.7 450737808
No.8 -1912969424
No.9 21899
```
:::info
搞不懂為什麼不會出現 segmentation fault 或是 out of range 的錯誤?
:::
#### Out-of-bounds Write
測試:
```c
#include <stdio.h>
int main()
{
int a[3];
a[0]=0;
a[1]=1;
a[2]=2;
a[3]=3;
printf("%p\n", &a[0]);
printf("%p\n", &a[1]);
printf("%p\n", &a[2]);
printf("%p\n\n", &a[3]);
printf("%d\n",a[3]);
return 0;
}
```
```
0x7fff0547ca7c
0x7fff0547ca80
0x7fff0547ca84
0x7fff0547ca88
3
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)
```
真的可以存取到超過宣告的位置,所以如果我想要存取某一段位置,可以事先算好那個位置在哪裡,然後用 out of bound 的方式拿到?
```c
#include <stdio.h>
int main()
{
int s=50,count=0;
int a[3];
a[-1]=-1;
a[0]=0;
a[1]=1;
a[2]=2;
a[3]=3;
printf("a[-1]:%p\n",&a[-1]);
printf("a[0]:%p\n", &a[0]);
printf("a[1]:%p\n", &a[1]);
printf("a[2]:%p\n", &a[2]);
printf("a[3]:%p\n\n", &a[3]);
count=(&s-a);
printf("count:%d\n",count);
printf("address of a:%p\n",a);
printf("address of count:%p\n",&count);
printf("address of s:%p\n",&s);
printf("address of a[count]:%p\n",&a[count]);
printf("a[count]:%d\n\n",a[count]);
a[count]=999;
printf("reassigned a[count]:%d\n",a[count]);
printf("reassigned s:%d\n",s);
return 0;
}
```
```
a[-1]:0x7fff6ce94ed8
a[0]:0x7fff6ce94edc
a[1]:0x7fff6ce94ee0
a[2]:0x7fff6ce94ee4
a[3]:0x7fff6ce94ee8
count:-2
address of a:0x7fff6ce94edc
address of count:0x7fff6ce94ed8
address of s:0x7fff6ce94ed4
address of a[count]:0x7fff6ce94ed4
a[count]:50
reassigned a[count]:999
reassigned s:999
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)
```
當存取 a[-1] 就已經取到先宣告的 count 的地址。
之後經過刻意的運算可以利用陣列取道任意想要的地址,甚至修改裡面的值,像上面的測試就更改了 s 的值。
CVE 看起來有點像程式版的聊齋誌異XD
-----
### week 3 --- test 2
```c
#include <stdio.h>
int endian() {
union { int a; char b;
} c = { .a = 1 };
return c.b == 1;
}
int main(){
int a=endian();
printf("%d\n",a);
return 0;
}
```
big endian 從高位取值,且 char 需要的空間較短,所以不會取到最低位的1。
[類似的例子](https://stackoverflow.com/questions/252552/why-do-we-need-c-unions)
```c
#include <stdio.h>
int main(){
union
{
int i;
float f;
} u;
// Convert floating-point bits to integer:
u.f = 3.14159f;
printf("As integer: %08x\n", u.i);
}
```
```
As integer: 40490fd0
```
這個程式是把 float 的格式輸出,3.14159~10~ = 40490FD0~16~ = 01000000 01001001 00001111 11010000~2~
-----
### week 2 --- test 3
[參考--Linux Signals – Example C Program to Catch Signals (SIGINT, SIGKILL, SIGSTOP, etc.)](https://www.thegeekstuff.com/2012/03/catch-signals-sample-c-code/)
```
void (*signal(int signo, void (*func )(int)))(int);
```
- signal 需要兩個參數
- signal 的第一個參數是 int 敘述 signal number
- signal 的第二個參數是 funtion pointer 敘述第一個參數發生該如何處理
- signal itself needs one integer and returns function pointer whose return type is void.
#### [案例 ---linux/tools/perf/ui/tui/setup.c](https://github.com/torvalds/linux/blob/6f0d349d922ba44e4348a17a78ea51b7135965b1/tools/perf/ui/tui/setup.c)
[24.2.2 Termination Signals](https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html)
```c
signal(SIGINT, ui__signal); #SIGINT is a program interrupt signal,
signal(SIGQUIT, ui__signal); #SIGQUIT is the dump core signal.
signal(SIGTERM, ui__signal); #The SIGTERM signal is a generic signal used to cause program termination.
```
```c
static void ui__signal(int sig)
{
ui__exit(false);
psignal(sig, "perf");
exit(0);
}
```
-----
### week 2 --- test 4
修改1
如果想要改變 ptrA 指向的位置,用指標的指標就可以修改到。
```c
#include <stdio.h>
int B = 2;
void func(int **p) { *p = &B; }
int main() {
int A = 1, C = 3;
int *ptrA ;
ptrA= &A;
printf("before: %p\n",ptrA);
func(&ptrA);
printf("after: %p\n",ptrA);
printf("address of B: %p\n",&B);
printf("%d\n", *ptrA);
return 0;
}
```
```
before: 0x7ffd0539bc68
after: 0x56344d7a1010
address of B: 0x56344d7a1010
2
```
修改2
如果想要改變A的值,就不需要用指標的指標。
```c
#include <stdio.h>
int B = 2;
void func(int *p) { *p = B; }
int main() {
int A = 1, C = 3;
int *ptrA ;
ptrA= &A;
printf("before: %p\n",ptrA);
func(ptrA);
printf("after: %p\n",ptrA);
printf("%d\n", *ptrA);
return 0;
}
```
```
before: 0x7ffc2e3af428
after: 0x7ffc2e3af428
2
```
結論:兩者結果不盡相同,看當時需要決定實做方式。
案例--[ linux/include/linux/rbtree_augmented.h
](https://github.com/torvalds/linux/blob/6f0d349d922ba44e4348a17a78ea51b7135965b1/include/linux/rbtree_augmented.h)
```c
static __always_inline struct rb_node *
__rb_erase_augmented(struct rb_node *node, struct rb_root *root,
struct rb_node **leftmost,
const struct rb_augment_callbacks *augment)
{
struct rb_node *child = node->rb_right;
struct rb_node *tmp = node->rb_left;
struct rb_node *parent, *rebalance;
unsigned long pc;
if (leftmost && node == *leftmost)
*leftmost = rb_next(node);
//... to be continue
```
leftmost 的部份。
-----
### week 3 --- test 3
```c
#include <stdint.h>
#include <stdio.h>
int parity(uint32_t x){
x ^= x >> 1;
x ^= x >> 2;
x = (x & 0x11111111U) * 0x11111111U;
return (x >> 28) & 1;
}
int main(){
uint32_t p=50;
printf("%d\n",parity(p));
return 0;
}
```
32bits 四個一組,右移一位做 xor 可以判斷相鄰兩個 bits 相加後的奇偶,在右移兩位做 xor 可以判斷相鄰四位 bits 相加後的奇偶。
(x & 0x11111111U) 最小的 bit 會代表包括自己還有相鄰較大3個bits的奇偶數,因此每四個取最小 bit。
最後乘以0x11111111U,可以發現所有的 bit 都會在第29個 bit 相加,故右移28 bits 。
[參考 更快的實作機制](https://stackoverflow.com/questions/43883473/working-inline-assembly-in-c-for-bit-parity)
```c
uint parity64(uint64 n){
unsigned char result = 0;
n = (n >> 32) ^ n;
n = (n >> 16) ^ n;
n = (n >> 8) ^ n;
__asm__("test %1, %1 \n\t"
"setp %0"
: "+r"(result)
: "r"(n)
:
);
return result;
}
```