# 2018q3 Homework3 (review)
`contributed by <0xff07>`
# Week 2 測驗 `8`
以下程式碼改寫自 Linux 核心原始程式碼,請對照註解 (預期功能),指出實作上的問題,假設輸入 `value` 不會超過 32-bit。
```C
/*
* Convert a signed n-bit integer to signed 32-bit integer.
* Common cases are done through the compiler,
* the screwed things has to be done by hand.
*/
static int32_t snto32(uint32_t value, unsigned n) {
switch (n) {
case 8: return ((int8_t) value);
case 16: return ((int16_t) value);
case 32: return ((int32_t) value);
}
return value & (1 << (n - 1)) ? value | (-1 << n) : value;
}
```
該段程式位於 `linux/drivers/hid/hid-core.c` 當中
可以在 [git log](https://github.com/torvalds/linux/commit/08585e43d22802666a466af1ca5795085e74d60d) 中找到下列敘述:
> HID: core: don't use negative operands when shift
>
> The recent C standard in 6.5.7 paragraph 4 defines that operands for
bitwise shift operators should be non-negative, otherwise it's an
undefined behaviour.
引述該章節:
> The result of `E1 << E2` is `E1` left-shifted `E2` bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is $E_1 × 2^{E_2}$, reduced modulo one more than the maximum value representable in the result type. If `E1` has a signed type and nonnegative value, and $E_1 × 2^{E_2}$ is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
可以發現將負數進行左移是未定義行為。
觀察一下程式註解預期的功能,可以發現該程式本質上在進行 sign extension:若第 n 個位元是 1,則要把最高位(也就是第 n)以上的位元通通設為 1; 反之則設為 0。若輸出的是 32 位元整數,則將 `-1 << n` 改成 `0xFFFFFFFF << n`,或是 commit 中的 `~0U << n` 即可。
在 CVE 中[查到](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-8326)一個類似的問題。修改該程式的 [commit](https://github.com/jsummers/imageworsener/commit/a00183107d4b84bc8a714290e824ca9c68dac738)也幾乎如出一徹。
:::success
延伸問題:
1. 參照 Linux 核心原始程式碼 (以及 git log,探討對應的修正;
2. 在 [Common Vulnerabilities and Exposure](https://cve.mitre.org/) (CVE) 找出類似的安全弱點,並且進行案例分析;
:::
---
# Week 3 測驗 `2`
考慮以下程式碼,在 little-endian 硬體上,會返回 `1`,反之,在 big-endian 硬體上會返回 `0`:
```C
int main() {
union { int a; char b;
} c = { .a = K1 };
return c.b == K2;
}
```
補完上述程式碼。
==作答區==
K1 = ?
* `(a)` 0
* `(b)` 1
* `(c)` -1
* `(d)` 254
K2 = ?
* `(a)` 0
* `(b)` 1
* `(c)` 254
在研讀整數相關部分的規範時,第 6.2.6.2 第 2 段提到:
> For signed integer types, the bits of the object representation shall be divided into three groups: value bits, padding bits, and the sign bit. There need not be any padding bits; there shall be exactly one sign bit.
>
> Each bit that is a value bit shall have the same value as the same bit in the object representation of the corresponding unsigned type (if there are M value bits in the signed type and N in the unsigned type, then M ≤ N). If the sign bit is zero, it shall not affect the resulting value. If the sign bit is one, the value shall be modified in one of the following ways:
>
> — the corresponding value with sign bit 0 is negated (sign and magnitude);
>
> — the sign bit has the value −(2N ) (two’s complement);
>
> — the sign bit has the value −(2N − 1) (ones’ complement).
>
> Which of these applies is implementation-defined,
這表示 C99 沒有強制規定要用 2's complement 嗎?
:::success
延伸問題: 解釋運作原理,並找出類似的程式碼
:::
---
# Week 3 測驗 `3`
以下程式碼計算 parity bit:
```C
#include <stdint.h>
int parity(uint32_t x)
x ^= x >> 1;
x ^= x >> 2;
x = (x & 0x11111111U) * 0x11111111U;
return (x >> P) & 1;
}
```
補完程式碼
==作答區==
P = ?
* `(a)` 32
* `(b)` 31
* `(c)` 30
* `(d)` 29
* `(e)` 28
* `(f)` 27
* `(g)` 26
* `(h)` 25
* `(i)` 24
答案為 (e)。
首先可以證明:把所有位元 XOR 起來,若位元中有奇數個 1,則結果將是 1; 反之若有偶數個 1,則結果將為 0。證明可以使用數學歸納法:
`base case`:只有一個位元 (0 或 1) 顯然對。
`induction`:由 XOR 的定義立刻得到成立。列表討論如下:
|第 n 位 |前 n - 1 位計算結果 1,且有奇數個 1|前 n - 1 位計算結果 0 ,且有偶數個 1|
|---|---|---|
|1|奇數 + 1 = 偶數,且 1 ^ 1 = 0|偶數 + 1 = 奇數,且 1 ^ 0 = 1|
|0|奇數 + 0 = 奇數,且 0 ^ 1 = 1|偶數 + 0 = 偶數,且 0 ^ 0 = 0|
因此,解決這個問題的其中一個方向,是找出所有位元的 XOR。而該程式正**計算了所有位元進行 XOR 的結果**。說明如下:
假定(用類似 verilog 的敘述方式):
$$
x[31:0]
$$
是原先希望計算 parity bit 的數。首先經過:
```c
x ^= x >> 1;
```
假定這時候的結果令為 $x_2[31:0]$,則有:
$$
\begin{align}
x_1[0] &= x[0] \oplus x[1] \newline
x_1[1] &= x[1] \oplus x[2] \newline
x_1[2] &= x[2] \oplus x[3] \newline
x_1[3] &= x[3] \oplus x[4] \newline
\dots\newline
x_1[30] &= x[30] \oplus x[31] \newline
\end{align}
$$
類似地,進行到下一個敘述:
```c
x ^= x >> 2;
```
假定這時候的運算結果為 $x_3$,則類似地,有:
$$
\begin{align}
x_2[0] &= x_1[0] \oplus x_1[2] = x[0] \oplus x[1] \oplus x[2] \oplus x[3]\newline
x_2[4] &= x_1[4] \oplus x_1[6] = x[4] \oplus x[5] \oplus x[6] \oplus x[7]\newline
\dots\newline
x_2[28] &= x_1[28] \oplus x_1[30] = x[28] \oplus x[29] \oplus x[30] \oplus x[31]\newline
\end{align}
$$
也就是一般地說:
$$
x_2[4i] = x[4i]\oplus x[4i + 1] \oplus x[4i + 2] \oplus x[4i + 3]
$$
因此,只要再計算:
$$
\bigoplus_{i = 0}^{7} x_2[4i]
$$
即可。接下來觀察:
```c
x = (x & 0x11111111U) * 0x11111111U;
```
當中 `x = (x & 0x11111111U)` 把上述 $x_2[4i]$ 取出來,接著乘上 `0x0x11111111U`,轉成二進位觀察為 `0001...00010001`。這個步驟可以視為:
```c
(x) + (x << 4) + (x << 8) + (x << 12) + ... + (x << 28);
```
因為是加法,所以套用加法器的原理,第 28 個位元就是 $x_2[28]$, $x_2[24]$ ... $x_2[0]$ 進行 XOR 的結果。由前面對 $x_2[4i]$ 的觀察可知,這樣做之後,第 28 位元其實也就是將所有位元 XOR 起來的結果。因此將第 28 位元取出來即可。
另外一個思考方式是在取出 $x_2[4i]$ 之後,計算:
$$
\sum_{i = 1}^{7}x_2[i]
$$
再看結果是奇數或是偶數(取第一個位元)就可以了。
因此在進行:
```c
(x) + (x << 4) + (x << 8) + (x << 12) + ... + (x << 28);
```
之後,最左邊 [31:28] 形成的整數之奇偶,或說第 28 個位元是 1 或是 0,就是 parity bit。
:::success
延伸問題: 解釋原理,並且提出更快的實作機制 (提示: 透過 SIMD)
:::