大学

這代碼看著就很危險

我能看到的就已經好幾個UB了

这个,char *password这行对吗

多次聲明不是問題, 但是沒必要重複聲明

哦查了下原来在哪都可以声明函数,我一直习惯写在全局(或者说定义声明在同一个层级,你都定义在全局了)就以为不能这么搞

C不管的

我的敲代码能力堪比一根成年香蕉。。。

大佬可否指正一下:grinning_cat:

GPT 6.1 Sol xHigh 簡單評價一下
其實你如果用C的標準庫 printf 和 scanf 本身就是漏洞百出的, 用好很困難
然後你再做 字符的 arithmetic calculation 就更容易錯, 這個地方都是天坑, 憑感覺就知道有錯誤
不過這些玩意兒… 初學者暫時不用管, 先能跑就行

– 分割線 –

主要問題是:輸入失敗後使用未初始化資料、整數溢位,以及缺乏字元與位移值的驗證。 以下區分可由目前 main() 觸發的問題,以及函式被其他程式呼叫時才會出現的風險。數值範例假設 ASCII 與 32 位元 int。

  1. 沒有檢查 scanf() 的回傳值:未初始化讀取,可能連帶造成記憶體越界

    scanf("%99s %d", x, &y);
    

    輸入 abc hello,或只有 abc 後就結束輸入時,只有 x 被成功賦值,y 仍未初始化;接著呼叫 password(x, y) 就會使用不確定的值。

    若一開始就遇到 EOF,x、y 都未初始化。此時 x 沒有可靠的 '\0' 終止標記,迴圈可能越界讀取、修改堆疊記憶體,後面的 %s 輸出也可能越界讀取,造成崩潰或資訊洩漏。

    至少必須在回傳值不等於 2 時停止處理;只把變數初始化,仍未解決接受無效輸入的問題。

  2. %d 本身無法安全處理超出 int 範圍的輸入

    例如:

    abc 999999999999999999999999999999
    

    依 C11/C17 的規則,若數值轉換結果無法由目標物件表示,行為未定義;不能假設 scanf() 一定會回傳失敗。因此,即使補上回傳值檢查,也還沒完整修好。 C 標準草案 N1570,§7.21.6.2¶10

    應先以有長度限制的方式讀入文字,再使用 strtol(),檢查是否有數字、ERANGE、INT_MIN/INT_MAX 範圍,以及結尾是否有非法字元。

  3. 三處運算可能發生有號整數溢位

    運算 觸發例子 問題
    'z' - b a -2147483648 122 - INT_MIN 超出 int 範圍
    a[i] - 'a' + b z 2147483647 25 + INT_MAX 溢位,之後才做 % 26 已經太晚
    a[i] + b 輸入位元組 0x80,位移 -2147483525,且 char 為有號 -128 + (-2147483525) 下溢

    有號整數溢位屬於未定義行為,不能依賴它自然繞回。SEI CERT INT32-C

    (int)a[i] 無法避免這些問題,也不會把負的 char 轉成正值。應在進行字元運算之前先縮小位移範圍:

    b %= 26;
    if (b < 0)
        b += 26;
    

    這段對 INT_MIN 也安全。

  4. 沒有驗證字元範圍,負位移也沒有正確循環

    函式似乎預期處理小寫英文字母,實際上卻接受所有非空白位元組:

    輸入 實際輸出 影響
    a -1 反引號 ` 沒有循環回 z
    Z 1 [ 大寫字母被錯誤轉換
    a -97 空字串 a[0] 被寫成 '\0',printf() 提早結束輸出
    a -70 ESC,位元組 0x1B 輸出可能包含終端控制字元

    中文/UTF-8 也會被逐位元組修改,破壞編碼。如果輸出進入終端或日誌,控制字元還可能干擾顯示或後續處理,實際影響取決於接收端。

    另外,運算結果可能超出 char 可表示的範圍;對有號 char,C11/C17 規定這類轉換的結果或訊號由實作決定。N1570,§6.3.1.3

    應明確決定:拒絕非小寫字元,或保留它們不變;接著只對合法字元做位移。

  5. 長度限制避免了直接寫爆,卻沒有完整驗證輸入

    %99s 最多儲存 99 個字元,再補一個 '\0',所以與 char x[100] 的容量配合正確。

    問題是超出的部分留在輸入串流中:

    • 超長字串剩餘部分若不是數字,後面的 %d 失敗,觸發第 1 點。
    • 若輸入為「連續 99 個 a,緊接著 1 2」,%d 會把緊接的 1 當成位移,真正的 2 留下。
    • abc 1garbage 也會成功完成兩次賦值,非法尾巴被忽略。
    • %s 無法讀取含空白的完整密碼;來自檔案或管線的內嵌 NUL,也可能造成讀入內容與處理內容不一致。

    若要求固定的輸入格式,就必須檢查整筆輸入、拒絕超長內容,並驗證分隔與剩餘字元。

  6. password() 的記憶體安全依賴呼叫者保證

    char *password(char *a, int b)
    

    函式沒有容量或長度參數。呼叫者必須保證 a 指向有效、可寫,而且在物件邊界內有 '\0' 的緩衝區。

    傳入 NULL、已釋放的指標、沒有終止字元的陣列,或需要修改的字串常值,都可能導致非法存取。對極大的合法字串,int i 還可能溢位。

    這些不是目前 main() 成功讀入後必然存在的漏洞;若要把函式提供給其他程式使用,就應清楚訂定契約,或增加容量/長度參數、以 size_t 索引並限制存取範圍。

  7. 若用來保護真正的密碼,演算法不具安全性

    對預期的小寫循環位移而言,這是凱撒密碼,只有 26 種位移,很容易還原,也不具備完整性保護。

    登入密碼應交給成熟的密碼雜湊函式庫處理;需要還原的機密資料則應使用成熟的認證加密方案。

我已用 MemorySanitizer 重現錯誤輸入與 EOF 造成的未初始化讀取,並用 UndefinedBehaviorSanitizer 重現表中三處整數溢位。以上能確認程式存在未定義行為;能否進一步利用,仍取決於實際編譯與執行環境。

英语里学士和单身狗是一个词……

确实,但英语语境下bachelor有一种“aspiring to find a spouse”的意味在

如果一个人不想找男女朋友,一般直接说single(说bachelor也没错,但美国人会觉得你很奇怪)

這個在哲學初階課用來講equivocation的時候經常舉例子