owned this note
owned this note
Published
Linked with GitHub
# 2025-03-25 問答簡記

> [封面故事](https://www.facebook.com/groups/system.software2025/posts/951584013845067/)
## 學習程式設計的心境變化
學習程式設計,不僅是為了寫程式,更是為了精確指導語言模型完成任務。過去,我需要花數天講解語法,但學生無法立即看到成果;現在,我們的學習方式也許會顛倒過來 —— 先看到成果,再學習它是如何達成。
回顧歷史,1960 年代,程式設計從穿孔卡片(當時的工程師必須在實體卡片上逐字元打孔)轉向鍵盤與終端機,讓程式設計變得更容易。每當技術門檻降低,應該有更多人學習,而非更少。諾貝爾獎得主 [Herbert Simon](https://en.wikipedia.org/wiki/Herbert_A._Simon) 當年對此的觀點,如今依然適用。
數十年來,程式設計從組合語言發展到 C 語言,從桌面端轉向雲端,從純文字編輯器進步到 IDE,再到如今的 AI 輔助開發。2023 年初,AI 輔助程式開發技術驚艷登場,開發出的程式完整度之高,超乎想像,並且具備電腦的速度優勢,讓人有進入下一世紀的感覺。過去,工程師遇到不熟悉的函式或作法時,必須先 Google 或查閱論壇,各種研究與測試往往消耗大量時間。但 AI 在短短兩年間突飛猛進,輔助程式開發已成為 AI 工具的標準功能,工程師只需清晰表達需求,讓 AI 產生對應的程式碼,開發效率大幅提升。擁抱科技,正帶來工程師最美好的時代。
最近,矽谷工程師間流行起一種新的程式設計思維:vibe coding (氛圍程式設計)。這個詞由 OpenAI 共同創辦人、電腦科學家 Andrej Karpathy 創造,指的是順應直覺、擁抱 AI 帶來的進步,甚至忘記程式碼的存在。在這種模式下,開發者完全沉浸於創作氛圍,透過 AI 工具自動化大量程式設計工作,從而減輕繁重的開發負擔,提高開發效率。如今的大型語言模型(如 Cursor Composer 搭配 Sonnet)已經強大到足以產生高品質程式碼,甚至可以透過 SuperWhisper 與 Composer 直接語音對話,幾乎不需要敲鍵盤。遇到錯誤訊息時,只需複製貼上,通常就能解決問題。許多開發者甚至發現,當 AI 無法修正錯誤時,透過隨機修改、迴避問題,直到錯誤消失,也是一種常見策略。
在短期專案或週末開發中,這種方式相當有效。例如,在建立一個網站或應用程式時,開發者並不是在傳統意義上「編寫程式」,而是透過檢視結果、描述想法、執行操作,並進行複製貼上,大多數情況下,這樣就能產出可運行的系統。然而,這樣的開發方式也帶來挑戰,因為程式碼的複雜度可能遠超使用者的理解範圍,真正發生問題時,仍需要工程師具備足夠的知識來分析和解決。
在現代軟體開發團隊中,擁抱 AI 不再只是選項,而是競爭優勢。如果團隊能夠妥善運用 AI,便能將開發模式轉型為「需求整理與規格設計」的角色,將準確的開發指令交給 AI,讓工程團隊真正成為驅動技術創新的決策者,而非只是在鍵盤上手動編寫程式碼。這樣的轉變,將使開發效率提升數倍,就像如虎添翼。
當然,AI 輔助開發並非沒有缺點。目前,由於伺服器環境與程式語言版本的差異,仍然需要工程師進行調整與優化,但對有經驗的開發者來說,這樣的調整並不困難。隨著 AI 持續進步,未來 AI 甚至有可能直接為不同環境自動配置系統,讓寫程式變得如同變魔術一般,令人驚嘆。
善用 AI 工具的技術型人才,將成為「10 倍專業人士」(10x professionals),他們在自身領域能發揮超乎尋常的影響力。對多數人來說,最好的方式不是單純作為 AI 工具的消費者,而是學習足夠的程式設計知識,來有效運用 AI 輔助的開發工具。
Kevin Kelly 是《WIRED》雜誌創辦人,同時也是當代著名的科技思想家與文化評論者。他的作品橫跨科技、文化與哲學,被譽為「未來學家」。2023 年 3 月,電子報作家 Noah Smith 與他進行一場深刻訪談,主題圍繞「AI 與人類心智」,以下摘錄。
> [30 年後,不會再有「AI」這個詞](https://www.facebook.com/chiukaun/posts/pfbid0TexnbHXV5SLwFeBWYCYYMHWp67qv49zXfzsackA5VjGtzRbkT3ii255LYQrg17Tnl)
AI 會有自我意識嗎?Kevin 認為會,電腦可模擬任何意識體,但運作基質不同,自我意識的表現也會不同。只要 AI 還在晶片上運作,它的幽默與創意總會有點「怪」。理解 AI 最好的方式,就是把它們當作外星智慧,如《星際迷航》的史巴克(Spock)。他甚至預測,未來「有自我意識的 AI」將被視為劣質品,真正昂貴的 AI 則會強調「無意識、只執行」。畢竟,我們希望它開車,而不是開到一半爆粗口。
AI 不會只有一種,也不會以 AGI(通用人工智慧)為終點。各類專業型 AI 將比 AGI 更強大,並根據功能與任務發展出不同「性格」。圖像生成領域已見雛形,不同創作者會偏好不同模型,就像選擇合作對象。未來的 AI 更像助理、夥伴,甚至寵物,需要花時間訓練。有人會比別人強 10 倍、甚至 100 倍,這些人將成為「AI 寶可夢大師」。截至 2023 年初,尚未有 AI 真正取代任何職業,多數失業焦慮來自想像。
生成式 AI 就像實習生,大多只能產出平庸內容,必須經過檢驗與修正。由於訓練素材來自人類平均作品,因此也繼承人類的偏見與錯誤。AI 應該要比我們更有道德——技術上並不困難,只是多加幾行程式碼而已。真正的挑戰在於定義何謂「更有道德」,甚至是「我們」是誰。人類對道德的認識往往粗糙且雙標,如何創造一個具有道德感的 AI,將成為巨大課題。
Kevin 指出,AI 最深遠的影響之一,是讓我們重新思考「什麼是人類心智」。AI 模擬行為讓我們發現許多看似人類獨有的活動,如下棋、寫作、繪畫,其實都具高度機械性。目前,我們已能成功模擬「模式識別與配對」等心智運作方式,未來隨著更多模型被建立,對心智的理解將遠超心理學與神經科學在過去數百年的貢獻。
Kevin 認為科技不只是工具,更是類似生物的「科技體」,一個具備自主傾向的系統。科技之間互相依賴、共同演化,形成超越個別元素的網絡整體。他不斷自問:「科技體渴望什麼?」
科技體第一個渴望是「複製」:生物靠基因複製延續,科技則透過資訊複製傳播。Kevin 說:「資訊渴望副本。」任何可被複製的資訊,終將被複製。從音樂到藝術作品,智財權與 AI 訓練資料的爭議,其實是人類在與整個科技體的本能抗衡——終將失敗。擁抱副本,才是未來。
科技體第二個特徵是「同步發明」:科技創新往往並非孤立事件,而是多點同時發生。愛迪生發明燈泡前,有 23 人曾獨立做出類似的電燈裝置;電話的發明人若非貝爾,也會是 Elisha Gray(兩人甚至同日申請專利)。微積分、蒸汽機、電腦亦然。創新不是來自孤高天才,而是整個科技網絡的必然結果。
就如我們不再說「智慧型手機」,未來的 AI 將無所不在,甚至不再被單獨命名。從未出現「AI 專家」的今天來看,對想投入這個領域的人是好消息——因為起跑點是一樣的。Kevin 預測,未來九成真正有影響力的 AI 不會被大眾注意,就像水管與電線一樣關鍵卻無感。
他的建議是:去研究那些還沒有明確名稱的東西。語言尚未定義的領域,是創造的起點。反之,如果你研究的領域早已有標準與體系,那你注定只是跟在他人之後。
20 到 30 歲,是冒險的黃金年代。你該用來做些看似荒謬、怪誕、與主流成功無關的事情。那些外表看來失敗的經驗,會是未來最堅實的基礎。
> [The Software Engineering Identity Crisis](https://annievella.com/posts/the-software-engineering-identity-crisis/)
我們成為軟體工程師,多半是因為熱愛「建造」。用我們的雙手、頭腦與程式碼創造事物,而非管理、監督。這份建造者的身份,是我們職業自我認同的核心。但這份身份正面臨深刻轉變。
AI 程式開發助手的崛起,改變我們撰寫程式的方式,也正重塑我們是誰。我們從創造者轉為協調者,從建造者轉為監督者。過去我們曾說,軟體工程遠不止於程式撰寫:需求分析、系統設計、測試、維運,都是我們專業的一部分。然而,產業卻推動我們專注於寫程式碼,把其他工作交給專業角色,如產品負責人、架構師、品質工程師等。而如今,當我們在程式開發技藝上精益求精,AI 卻開始取代我們最自豪的部分。
這不只是工作轉型,而是身分轉變。寫程式的樂趣,在於用邏輯與創意解決問題:追蹤難解的 bug、優化效能、重構程式碼,這些都是工程師認同感的來源。當 AI 取代這些任務,我們是否仍是工程師?
科技產業正在快速轉變。Google 指出,四分之一的新程式碼來自 AI;Y Combinator 執行長 Garry Tan 表示,有些新創企業 95% 的程式碼由 AI 撰寫。這是一種從「寫解法」轉為「寫提示」的職業轉變。AI 時代,與其掌握語法,我們要學會表達意圖、給出上下文、設計正確提示,甚至得「禮貌對待」AI —— 就像對待團隊成員。這不再只是技能轉變,而是角色轉變。
這樣的變化已經有新名稱:"vibe coding" —— Andrej Karpathy 所提出,代表一種與 AI 共振的程式開發方式。我們不再精雕細琢每一行程式碼,而是描述目標,讓 AI 產出結果。我們轉向「做什麼」而非「怎麼做」。Karpathy 形容:「完全沉浸於氛圍,擁抱指數增長,忘記程式碼的存在。」但若忘記程式碼,我們還是工程師嗎?
軟體工程向來不只是撰寫程式,而是解決問題、創造價值、建造有意義的東西。這些核心能力,Addy Osmani 稱之為「持久的工程技能」,如系統思考、溝通、判斷與驗證能力 —— 在 AI 時代反而更重要。
但這也造成職涯焦慮。當我們不再親手寫程式,而是與 AI 合作,我們還是建造者嗎?還是變成技術管理者的變形?這正是工程師普遍面對的「身分危機」。與其僅做監督者,我們是否能「擺盪」 —— 在深入技術與策略協調之間找到平衡?如同工程師與管理者角色的轉換,AI 時代也需要我們在手工匠人與系統設計者之間來回遊走。這不是身分的喪失,而是拓展。
AI 是工具,不是敵人。掌握它、理解它的限制,才是真正的關鍵。如同工業革命時代的匠人,有些人轉型為能修機器、改進工序的專業人士 —— 他們保留對材料與流程的深刻理解。我們也應如此。
然而,這轉變並不容易。人機協作研究指出,人們對 AI 的信任常從高點開始,然後迅速崩塌。AI 錯誤回應、幻覺、缺乏透明性,會讓團隊生產力下降。若工程師無法有效協調 AI,就像不信任的主管領導團隊,反而造成阻礙。
這不是假設,而是現實。GitClear 研究顯示,AI 編碼普及後,重複貼上程式碼增長 17.1%,程式碼變更在兩週內被改動的比率增至 5.7%。程式碼品質下降,維護成本上升,這提醒我們:速度不能取代技藝。
那麼,我們該怎麼做?或許三條路並存:
- 抵制:專注人類創意仍不可取代的領域
- 適應:成為 AI 系統的設計與協調者
- 平衡:在建造與監督之間擺盪
我們的本質,不在於寫多少程式碼,而是解決多少問題。AI 可以幫我們「做」,但我們仍要「想」。不論是 prompt、架構、還是整體設計 —— 人仍是主體。掌握 AI,不只是掌握工具,而是重新掌握工程的全貌。
這也是開放原始碼、實體聚會、教育等傳統價值的延伸思考:若程式碼都由 AI 產生,程式開發的素養還有意義嗎?聚會討論程式還必要嗎?教育該如何調整?我們逐漸意識到,技術的進展未必引導我們走向原本預期的未來,而是開啟一條全新的路。我們需要的不是放棄,而是重構對專業的理解。是的,AI 能加速學習,協助解題;但人類的思辨、價值判斷與創造力無法簡化為 prompt。正如歷史反覆告訴我們:那些能適應新工具的人,不只是生存者,更是塑造未來的人。
未來的軟體工程師不會只寫程式碼。他們會是「AI 增強的建築師」,能運用技術、理解系統、整合人機協作,打造穩固、可維護、有意義的數位建築。問題不在於 AI 是否取代我們,而在於我們是否願意成為比以前更大的自己。
## 快訊
* [CD 的糾錯機制,即 CIRC](https://www.facebook.com/groups/system.software2025/posts/951762333827235/): [Cross-Interleaved Reed-Solomon Code](https://en.wikipedia.org/wiki/Cross-interleaved_Reed%E2%80%93Solomon_coding),[Reed-Solomon 碼](https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction) (RS code) 特別適用於同時處理隨機錯誤(random errors)與突發錯誤(burst errors)。搭配閱讀: [錯誤更正碼 (ECC) 介紹和實作考量](https://hackmd.io/@sysprog/r1wrjOp6a)
* [藉由量子電腦,產生真正隨機亂數並獲得數學驗證的演算法](https://www.facebook.com/groups/system.software2025/posts/951776020492533/)
* [Donald Knuth 開創演算法分析領域](https://www.facebook.com/groups/system.software2025/posts/951644030505732/)
* [運用 PortAudio 函式庫改寫 semu 現有 virtio-snd 的程式碼](https://www.facebook.com/groups/system.software2025/posts/951570733846395/)
* [CIH 以純腳本破解 Android 手機,取得超級使用者權限](https://www.facebook.com/groups/system.software2025/posts/951208003882668/)
* 你好手好腳,為何不能去 Google?校友經驗分享: [案例 1](https://www.facebook.com/groups/system.software2025/posts/950832553920213/), [案例 2](https://www.facebook.com/groups/system.software2025/posts/949452387391563/)
* [SSE2NEON 專案收到來自 Microsoft 的貢獻,支援 Arm64EC](https://www.facebook.com/groups/system.software2025/posts/950827373920731/)
* [Linux v6.14 發布](https://www.facebook.com/groups/system.software2025/posts/950206797316122/)
* [修正 shecc 在的若干指標存取錯誤](https://www.facebook.com/groups/system.software2025/posts/949790577357744/)
* [以 BSD 授權條款釋出 AlexNet](https://www.facebook.com/groups/system.software2025/posts/949428644060604/)
* [由 Linux memory mapping 的主要維護者撰寫的新書《Linux Memory Manager》,1300 頁](https://www.facebook.com/groups/system.software2025/posts/949424597394342/)
* [Mediatek 已獲得NVIDIA 的 NVLink/Serdes 224/AEC 技術使用許可](https://www.facebook.com/groups/system.software2025/posts/949421837394618/)
* [Riverside Research 收購 Cog Systems,擴展政府與關鍵任務應用領域的端對端網路安全能力,針對國家安全客戶的安全行動與嵌入式系統](https://www.facebook.com/groups/system.software2025/posts/951781937158608/)

> [翻身前,先健身](https://www.facebook.com/groups/system.software2025/posts/951186020551533/)
## EricccTaiwan
> `fix16_tanh()` 的結果為何通常只精確到小數點後三位?
> 回顧 [03-18 問答簡記](https://hackmd.io/EFs_Nfs6TOmIA5ldA_tZsQ)
## BennyWang1007
> pull request [Improve dudect test with cropped time analysis #283](https://github.com/sysprog21/lab0-c/pull/283)
## rota1001
> [fixed_sqrt 改進](https://hackmd.io/@rota1001/linux2025-homework3#fixed_sqrt-%E6%94%B9%E9%80%B2)
這是原本的 `fixed_sqrt` 函式:
```c
int fixed_sqrt(int x)
{
if (x <= 1 << Q)
return x;
int z = 0;
for (int m = 1UL << ((31 - __builtin_clz(x)) & ~1UL); m; m >>= 2) {
int b = z + m;
z >>= 1;
if (x >= b)
x -= b, z += m;
}
z = z << Q / 2;
return z;
}
```
會發現,它會先開完根號後再左移 $\frac{Q}{2}$,可見二進位制下最低的 $\frac{Q}{2}$ 位元永遠是 0,這些是喪失的精度。
所以可以改成在開根號前先左移 `Q` 位再進行根號運算,會發現:
$$
\sqrt{(x\times2^Q)\times 2^Q}=\sqrt{x}\times 2^Q
$$
所以這樣能更準確的求出根號。修改後程式碼為以下:
```c
int fixed_sqrt_modified(int x)
{
if (x <= 0)
return 0;
int64_t k = (x << Q);
int z = 0;
for (int m = 1UL << ((63 - __builtin_clzll(k)) & ~1UL); m; m >>= 2) {
int b = z + m;
z >>= 1;
if (k >= b)
k -= b, z += m;
}
return z;
}
```
然後是另外提到的 `fixed_log` 的改進,原始程式碼中,對於一個 $C$,我們會找到 $A=2^m,\ B=2^{m+1}$ 使得 $A\le C \le B$,接下來進行指數二分搜尋。這在 $C \ge 1$ 的情況下會正常運作,但是試想 $C < 1$ 的情況,這樣找到的 $m$ 其實是負數。而以下是範例程式中拿來找 $A,B$ 的方式:
```c
Q23_8 Llog = (31 - __builtin_clz(y) - Q) << Q, Rlog = Llog + (1 << Q)
```
可以發現,當 $C < 1$ 的時候,代表 $y$ 的位數(`31 - __builtin_clz(y)`)小於 `Q`,也就是說 `(31 - __builtin_clz(y) - Q)` 會小於 0,所以這個左移操作會是未定義行為。順道一題,這個程式有另外一點需要修改,就是在第一行的時候會將 `input` 從 `Q23_8` 轉換成 `Q15_16`,然而之後卻是將它當作 `Q23_8` 來使用,沒有做任何修正(再加上我們不應該期待 `input` 的最高 `Q` 位都是 0,如果一定要做這樣的操作應轉型成 64 位元整數型別,並對結果依照對數律調整)。於是我直接移除這個左移操作,這樣結果才會正確。
```diff
- Q23_8 y = input << Q; // int to Q15_16
+ Q23_8 y = input;
```
然後接下來改進這個 `fixed_log`,引入 [picolibc/newlib/libm/math/s_log.c](https://github.com/picolibc/picolibc/blob/main/newlib/libm/math/s_log.c) 中的作法。
這裡先寫一個 $\ln$ 的泰勒展開式備用:
$$
\displaystyle \ln x = x - \frac{x^2}{2} + \frac{x^3}{3} - \frac{x^4}{4}+...
$$
首先簡單統整一下這個作法的總體概念:
- 使用接近 1 的輸入進行逼近
- 盡可能刪除偶次項或奇數項以在相同成本下進行更高次的逼近
我們現在要求 $\ln(1+f)$,然而,我們可以消除 $x$ 奇數羃的項,讓我們可以用相同的成本計算到更高的羃。
我們可以找到一個 $s$ 使得:
$$
\displaystyle \ln(1+f) = \ln(\frac{1+s}{1-s})= \ln(1+s)-\ln(1-s) = 2\sum_{i=0}^{\infty}\frac{1}{2i+1}s^{2i+1}
$$
由 $\displaystyle 1+f = \frac{1+s}{1-s}$ 可以求得 $\displaystyle s=\frac{f}{2+f}$,而我們會把 $f$ 控制在 $[1, 2)$ 這個區間中(其實就是把它位移到特定位數,並且紀錄位移的量在最後做調整),所以可以把 $s$ 視為接近 0 的數(當然我們在高次項還是要做處理)
我們可對 $\ln(1+f)$ 做以下處理:
$$
\displaystyle \ln(1+f) = 2\sum_{i=0}^{\infty}\frac{1}{2i+1}s^{2i+1}=2s + sR
$$
其中,$R$ 的部份我們會用一個 14 次的多項式去逼近。雖說是 14 次,但它只有偶數項,這樣能用比較少的項數去做到較高次數的逼近。至於這些係數是使用 [Reme algorithm](https://en.wikipedia.org/wiki/Remez_algorithm) 計算,可拿來求一個函數的多項式近似。
這裡直接使用上述專案中的常數:
```c
Lg1 = _F_64(6.666666666666735130e-01), /* 3FE55555 55555593 */
Lg2 = _F_64(3.999999999940941908e-01), /* 3FD99999 9997FA04 */
Lg3 = _F_64(2.857142874366239149e-01), /* 3FD24924 94229359 */
Lg4 = _F_64(2.222219843214978396e-01), /* 3FCC71C5 1D8E78AF */
Lg5 = _F_64(1.818357216161805012e-01), /* 3FC74664 96CB03DE */
Lg6 = _F_64(1.531383769920937332e-01), /* 3FC39A09 D078C69F */
Lg7 = _F_64(1.479819860511658591e-01); /* 3FC2F112 DF3E5244 */
```
這些常數可以轉換成符合我們要求的定點數的格式,在我的實作中,會在中間使用偏移 31 位的定點數,用 python 可以簡單的求出來。
```c
const uint64_t a1 = 1431655765, a2 = 858993459, a3 = 613566760, a4 = 477218078,
a5 = 390489239, a6 = 328862160, a7 = 317788895;
```
回顧一下上面推導出來的公式 $2s + sR$,可以發現 $2s$ 這項的精確度是可以再改良的。這裡可以透過用 $f$ 來表示 $s$ 來降低使用 $s$ 帶來精度損失(因為 $s$ 是 $f$ 透過除法操作得來的,再加上 $2s$ 其實就預設最低位元是 0)。於是我們發現:
$$
\displaystyle (1-s)f = \frac{2}{1+f}\times f = 2s
$$
於是可得 $2s = f-sf=f-\frac{1}{2}f^2(1-s)$
$\displaystyle \text{Let }h = \frac{f^2}{2}$,則:
$$
\displaystyle \ln(1+f)=f-h(1-s)+sR = f-(h-s(h+R))
$$
最後就是把東西全部代進去,然後依照一開始平移的位元數對結果做一些調整,至此完成,完整程式碼放在 [gist](https://gist.github.com/rota1001/6a13112a8ab8d85a1f95070077de8e38#file-fixed-log-c)。
順道一題,上述東西都是讀[註解](https://github.com/picolibc/picolibc/blob/main/newlib/libm/math/s_log.c#L17)讀出來的。
## nyraa
> unsigned int 有多大
> x86-64 ABI spec Page 11
> https://refspecs.linuxbase.org/elf/x86_64-abi-0.21.pdf
問題:為什麼 sizeof 跟 alignment 會有不一樣
> https://en.wikipedia.org/wiki/Red_zone_(computing)
[ASLR](https://en.wikipedia.org/wiki/Address_space_layout_randomization) 強度有限
閱讀:
* 依據〈[Exploiting Linux and PaX ASLR's weaknesses on 32- and 64-bit systems](https://www.blackhat.com/docs/asia-16/materials/asia-16-Marco-Gisbert-Exploiting-Linux-And-PaX-ASLRS-Weaknesses-On-32-And-64-Bit-Systems-wp.pdf)〉,之前 Linux 的 ASLR 蒙受 4 個弱點: (第 4 頁)
* Low entropy
* Non-uniform distribution
* Correlation between maps
* Memory layout inheritance
* [Demystifying ASLR: Understanding, Exploiting, and Defending Against Memory Randomization](https://securitymaven.medium.com/demystifying-aslr-understanding-exploiting-and-defending-against-memory-randomization-4dd8fe648345)
* [ASLRn't: How memory alignment broke library ASLR](https://zolutal.github.io/aslrnt/)
### Low entropy
在 Linux 中,載入器在載入的時候會把物件位置對齊 page , 普通 page 大小是 4KiB ,會對齊 12 bit , 2MiB 以上的函式庫例如 `libc` 會在記憶體中對齊 hugepage ,也就是 21 bit ,這會讓隨機位址的資訊熵降低,更容易窮舉出函式庫的記憶體位址,特別是在 32 位元的系統中,天生 Address space 就比 64 位元少,再來個對齊就至多剩下 $32 bit - 21 bit = 11 bit$ 可以作為隨機空間了。
底下這個圖表是第一篇文章中比較 Linux 跟 PaX 的 ASLR 的資訊熵, PaX 的資訊熵有小數點的原因是因為隨機數的分布不均勻導致的,下一個段落會提到怎麼個不均勻法。

在第三篇文章中說在某個時期,原本 `mmap` 只有 DAX 的記憶體會對齊 hugepage ,在某次 [patch](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1854bc6e2420472676c5c90d3d6b15f6cd640e40) 中改成大於 2MiB 的物件一律對齊 hugepage ,當時的 Ubuntu 預設的 32 位元程式的 `mmap` 的隨機範圍設定只有 8 位元,而從對齊 page 的 12 位元到對齊 hugepage 的 21 位元之間會損失 9 位元的隨機空間,讓那個作者在測試 32 位元程式的時候發現 `libc` 每次載入都會在同一個位址,相當於 ASLR 沒有作用,後來在 Ubuntu 的某次[更新](https://git.launchpad.net/~ubuntu-kernel/ubuntu/+source/linux/+git/noble/commit/?h=master-next&id=760c2b1fa1f5e95be1117bc7b80afb8441d4b002)中,提升了預設的隨機位元數量,在 AMD64 架構下的 32 位元程式預設的隨機範圍提升到 16 位元,來避免 32 位元的程式的 ASLR 失效的問題。
我的電腦目前系統使用 Ubuntu 24.04.2 ,版本如下:
```bash
❯ uname -a
Linux ubuntu-TravelMate-X514-51 6.11.0-17-generic #17~24.04.2-Ubuntu SMP PREEMPT_DYNAMIC Mon Jan 20 22:48:29 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
```
測試執行 32 位元的程式 1000 次,並將 `libc` 的位址全部做 OR 運算可以得到:
```bash
❯ python3 ./cat32_test.py
0xfffff000
```
其中 `libc` 大小約為 2.16MiB;
相同測試程式但是將觀測的函式庫改為 `ld` 會得到:
```bash
❯ python3 ./cat32_test.py
0xfffff000
```
其中 `ld` 大小約為 207.36KiB 。
兩者都只有尾數三位數是 0 從來沒變過,代表這個結果只有對齊 page 的 12 位元,且不論物件是否大於一個 hugepage 。
再來是 64 位元的測試,條件除了換 64 位元程式以外沒有差別:
```bash
# libc size about 2.03MiB
❯ python3 cat_test.py
0x7fffffe00000
```
```bash
# ld size about 231.07KiB
❯ python3 cat_test.py
0x7ffffffff000
```
可以看出大於 2MiB 的物件確實對齊了 21 位元也就是 hugepage;小於 2MiB 的物件就只對齊了 12 位元也就是 page 的大小。
### Non-uniform distribution
在第一篇文章的實驗中, Linux 跟 PaX 的 ASLR 位移分布不是均勻分布,雖然 PRNG 的產出結果是均勻分布的,但是在例如 PaX 32 中分布是三角形分布, PaX 64 的分布是鐘形分布。

原因來自於位移的隨機數是由兩個或三個均勻分布的隨機數相加得來的,兩個均勻分布的隨機數相加的分布就是三角形分布,三個均勻分布的隨機數相加就是鐘形分布。
因此如果窮舉猜測位址的時候猜測三角分布或是鐘形分布頂端的值,發生機率特別高的區域,猜中的機率會比較高,藉此減少攻擊成本。
### Correlation between maps
記憶體中物件的位址關係可以分成幾種:
#### Total correlation
兩個物件之間的位址關係是完全相關的,只要知道其中一個,就可以完全推測出另一個在哪裡,例如 Linux 跟 PaX 的動態鏈結函式庫在載入的時候會一個接一個排排站,只要知道其中一個就可以知道其他所有的函式庫在記憶體中的位置;
另一個例子是 Linux 中`randomize_va_space` 設定為 1 的時候, mmap base, stack, VDSO page 位址會被隨機化,但 Heap 位址不會隨機化,只要知道 Heap 中其中一個物件的位址,就可以知道其他所有物件的位址(`randomize_va_space` 預設為 2 ,也就是除了 1 中提到的那幾個項目之外 Heap 位址也會隨機化)。
#### Positive correlation
兩個物件之間的位址是有相關的,知道一個物件位址之後可以知道另一個物件的部分位址,像是其中幾位元的位址資訊。
例如在 64 位元的 Linux 中, Executable 區段跟 Heap 區段正相關, Heap 會在 Executable 加上 13 位元的隨機位移上, Executable 本身有 28 位元的隨機偏移範圍,所以當知道 Heap 的位址之後,窮舉搜尋 Executable 的範圍就從 28 位元減少到 13 位元。
#### Useless correlation
沒關係,意思就是兩個物件的位址之間沒有任何關係,知道一個之後對於知道另一個沒相關的物件位址毫無幫助。
### Memory layout inheritance
子行程在建立的時候會繼承親代行程的記憶體佈局,大多數情況下 `fork()` 完之後會馬上 `exec()` 替換掉這個子行程,但是有些軟體會有 pre-forking 的行為,例如 Apache 等網路伺服器,每個連線要求會用一個 fork 來處理, pre 的意思是會在連線進來之前先開好 fork ,連線進來之後才做事,這些 fork 有可能設計上會是如果死了會被親代行程重生,以保證現在準備好的 pre-fork 數量足夠,這樣的話用暴力搜尋子行程記憶體佈局就可以很完整,因為每次 fork 出來的子行程的記憶體佈局都相同,一次嘗試失敗被 kill 了還可以繼續對同一個佈局嘗試。
另外在 fork 出來的兄弟姊妹行程之間也可以互相知道對方的記憶體佈局,因為 ASLR 的演算法是確定性的,就算兄弟姊妹們之後對記憶體的 mmaps 操作序列不一樣,如果有惡意的兄弟姊妹行程也可以預測出其他兄弟姊妹的行為。
## yy214123
> [lab0](https://hackmd.io/@yy214123/linux2025-homework1)
使用 [taskset](https://man7.org/linux/man-pages/man1/taskset.1.html) 將程式碼的執行限制於給定的 CPU 核
搭配第二週教材: [分析「快慢指標」](https://hackmd.io/@sysprog/ry8NwAMvT)