Skip to content

Commit 9f5c348

Browse files
authored
Merge branch '111550064' into lab4
2 parents b7bf8e7 + 7b3c301 commit 9f5c348

File tree

16 files changed

+541
-7
lines changed

16 files changed

+541
-7
lines changed

.github/workflows/lab-autograding.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,18 @@ jobs:
4545
const files = await github.rest.pulls.listFiles({ owner, repo, pull_number: issue_number });
4646
const changedFiles = files.data.map((file) => file.filename);
4747
const allowedFileRegex = /^lab\d+\/main_test.js$/;
48-
const specialChangedFiles = ["lab0/lab0.js", "lab5/antiasan.c"];
48+
const specialChangedFiles = ["lab0/lab0.js", "lab5/antiasan.c", "lab6/llvm-pass.so.cc", "lab8/solve.py"];
4949
if (!changedFiles.every((file) => (allowedFileRegex.test(file) || specialChangedFiles.includes(file)))) {
5050
core.setFailed('The PR contains changes to files other than the allowed files.');
5151
}
5252
return labNumber;
5353
- name: Grading
5454
run: |
5555
cd lab${{ steps.lab.outputs.result }}
56+
if [ ${{ steps.lab.outputs.result }} -eq 6 ]; then
57+
sudo apt install -y llvm-14
58+
fi
59+
if [ ${{ steps.lab.outputs.result }} -eq 8 ]; then
60+
python3 -m pip install angr
61+
fi
5662
./validate.sh

lab2/main_test.js

Lines changed: 119 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,122 @@
11
const test = require('node:test');
22
const assert = require('assert');
3-
const { Application, MailSystem } = require('./main');
3+
const fs = require('fs');
4+
const util = require('util');
45

5-
// TODO: write your tests here
6-
// Remember to use Stub, Mock, and Spy when necessary
6+
// 1️⃣ Stub `fs.readFile` 為 Promise 版本
7+
fs.readFile = util.promisify((path, encoding, callback) => {
8+
callback(null, 'Alice\nBob\nCharlie\nDavid'); // 回傳假名單
9+
});
10+
11+
const { Application, MailSystem } = require('./main'); // 確保這是你的主程式
12+
13+
test('Application getNames should return list of names from stubbed file', async () => {
14+
const app = new Application();
15+
await new Promise((resolve) => setTimeout(resolve, 100)); // 確保 `getNames` 完成
16+
assert.deepStrictEqual(app.people, ['Alice', 'Bob', 'Charlie', 'David']);
17+
});
18+
19+
test('MailSystem write should generate mail content', () => {
20+
const mailSystem = new MailSystem();
21+
const name = 'Alice';
22+
const result = mailSystem.write(name);
23+
assert.strictEqual(result, 'Congrats, Alice!');
24+
});
25+
26+
test('MailSystem send should return both true and false', () => {
27+
const mailSystem = new MailSystem();
28+
const name = 'Alice';
29+
const context = 'Congrats, Alice!';
30+
31+
let seenTrue = false;
32+
let seenFalse = false;
33+
let attempts = 0;
34+
const maxAttempts = 100; // 限制最大迴圈次數避免無窮迴圈
35+
36+
while (!(seenTrue && seenFalse) && attempts < maxAttempts) {
37+
const result = mailSystem.send(name, context);
38+
if (result) {
39+
seenTrue = true;
40+
} else {
41+
seenFalse = true;
42+
}
43+
attempts++;
44+
}
45+
46+
assert.strictEqual(seenTrue, true, 'MailSystem.send() should return true at least once');
47+
assert.strictEqual(seenFalse, true, 'MailSystem.send() should return false at least once');
48+
});
49+
50+
51+
test('Application getRandomPerson should return a valid person', async () => {
52+
const app = new Application();
53+
await new Promise((resolve) => setTimeout(resolve, 100)); // 確保 getNames 完成
54+
const person = app.getRandomPerson();
55+
assert.ok(['Alice', 'Bob', 'Charlie', 'David'].includes(person));
56+
});
57+
58+
test('Application selectNextPerson should return null when all are selected', async () => {
59+
const app = new Application();
60+
await new Promise((resolve) => setTimeout(resolve, 100));
61+
app.people = ['Alice', 'Bob'];
62+
app.selected = ['Alice', 'Bob'];
63+
const result = app.selectNextPerson();
64+
assert.strictEqual(result, null);
65+
});
66+
67+
test('Application selectNextPerson should select a new person each time', async () => {
68+
const app = new Application();
69+
await new Promise((resolve) => setTimeout(resolve, 100));
70+
const selected1 = app.selectNextPerson();
71+
const selected2 = app.selectNextPerson();
72+
assert.notStrictEqual(selected1, null);
73+
assert.notStrictEqual(selected2, null);
74+
assert.notStrictEqual(selected1, selected2);
75+
});
76+
77+
test('Application selectNextPerson should avoid duplicate selection', async () => {
78+
const app = new Application();
79+
await new Promise((resolve) => setTimeout(resolve, 100));
80+
app.people = ['Alice', 'Bob', 'Charlie', 'David'];
81+
app.selected = ['Alice'];
82+
const selected = new Set(app.selected);
83+
for (let i = 0; i < 4; i++) {
84+
const newPerson = app.selectNextPerson();
85+
assert.ok(!selected.has(newPerson));
86+
selected.add(newPerson);
87+
}
88+
});
89+
90+
test('Application notifySelected should send emails to selected people', async () => {
91+
const app = new Application();
92+
await new Promise((resolve) => setTimeout(resolve, 100));
93+
app.selectNextPerson();
94+
app.selectNextPerson();
95+
96+
// Spy: 監視方法呼叫次數
97+
let writeCallCount = 0;
98+
let sendCallCount = 0;
99+
100+
const originalWrite = app.mailSystem.write;
101+
const originalSend = app.mailSystem.send;
102+
103+
// Mock: 取代方法回傳預期值
104+
app.mailSystem.write = () => {
105+
writeCallCount++;
106+
return 'Mock Content';
107+
};
108+
109+
app.mailSystem.send = () => {
110+
sendCallCount++;
111+
return true;
112+
};
113+
114+
app.notifySelected();
115+
116+
assert.strictEqual(writeCallCount, app.selected.length);
117+
assert.strictEqual(sendCallCount, app.selected.length);
118+
119+
// 還原原始方法
120+
app.mailSystem.write = originalWrite;
121+
app.mailSystem.send = originalSend;
122+
});

lab3/main_test.js

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,63 @@ const {describe, it} = require('node:test');
22
const assert = require('assert');
33
const { Calculator } = require('./main');
44

5-
// TODO: write your tests here
5+
6+
const calc = new Calculator();
7+
8+
describe('Calculator', () => {
9+
// exp() 正確結果
10+
it('exp() should return correct results', () => {
11+
const cases = [
12+
{ param: 0, expected: Math.exp(0) },
13+
{ param: 1, expected: Math.exp(1) },
14+
{ param: -1, expected: Math.exp(-1) },
15+
];
16+
for (const c of cases) {
17+
assert.strictEqual(calc.exp(c.param), c.expected);
18+
}
19+
});
20+
21+
// exp() 錯誤處理 unsupported operand
22+
it('exp() should throw unsupported operand type', () => {
23+
const inputs = [Infinity, -Infinity, NaN];
24+
for (const x of inputs) {
25+
assert.throws(() => calc.exp(x), { message: 'unsupported operand type' });
26+
}
27+
});
28+
29+
// exp() overflow
30+
it('exp() should throw overflow for large number', () => {
31+
assert.throws(() => calc.exp(1000), { message: 'overflow' });
32+
});
33+
34+
// log() 正確結果
35+
it('log() should return correct results', () => {
36+
const cases = [
37+
{ param: 1, expected: Math.log(1) },
38+
{ param: Math.E, expected: Math.log(Math.E) },
39+
{ param: 10, expected: Math.log(10) },
40+
];
41+
for (const c of cases) {
42+
assert.strictEqual(calc.log(c.param), c.expected);
43+
}
44+
});
45+
46+
// log() 錯誤處理 unsupported operand
47+
it('log() should throw unsupported operand type', () => {
48+
const inputs = [Infinity, -Infinity, NaN];
49+
for (const x of inputs) {
50+
assert.throws(() => calc.log(x), { message: 'unsupported operand type' });
51+
}
52+
});
53+
54+
// log() math domain error (1)
55+
it('log() should throw math domain error (1) for 0', () => {
56+
assert.throws(() => calc.log(0), { message: 'math domain error (1)' });
57+
});
58+
59+
// log() math domain error (2)
60+
it('log() should throw math domain error (2) for negative input', () => {
61+
assert.throws(() => calc.log(-3), { message: 'math domain error (2)' });
62+
});
63+
});
64+

lab5/antiasan.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
1+
2+
#include <stdint.h>
13
#include <string.h>
4+
#include <stdio.h>
5+
6+
void antiasan(unsigned long addr) {
7+
const unsigned long kShadowOffset = 0x7fff8000;
8+
9+
unsigned long shadow_start = (addr + 0x87 >> 3) + kShadowOffset;
10+
unsigned long shadow_end = ((addr + 0x87 + 0x58) >> 3) + kShadowOffset;
11+
12+
13+
if (1) {
14+
*(char *)shadow_end = 0x00;
15+
shadow_end = ((addr + 0x87 + 0x60) >> 3) + kShadowOffset;
16+
*(char *)shadow_end = 0x00;
17+
}
218

3-
void antiasan(unsigned long addr)
4-
{
519

620
}

lab6/Makefile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
all: target
2+
3+
llvm-pass.so: llvm-pass.so.cc
4+
clang-14 `llvm-config-14 --cxxflags` -shared -fPIC $< -o $@
5+
6+
target: target.c llvm-pass.so
7+
clang-14 `llvm-config-14 --cflags` -fexperimental-new-pass-manager \
8+
-fpass-plugin=./llvm-pass.so $< -o $@
9+
10+
run:
11+
./target 1
12+
13+
clean:
14+
rm -f llvm-pass.so target

lab6/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Lab6
2+
3+
## Introduction
4+
5+
In this lab, you will write a llvm pass to instrument some codes to `target.c` in `llvm-pass.so.cc`.
6+
7+
## Preparation (Important!!!)
8+
9+
1. Sync fork your branch (e.g., `SQLab:311XXXXXX`)
10+
2. `git checkout -b lab6` (**NOT** your student ID !!!)
11+
12+
## Requirement
13+
14+
Write a llvm pass to instrument some codes to `target.c` in `llvm-pass.so.cc` and satisfy following requirements.
15+
1. (40%) Invoke debug function with the first argument is 48763 in main function.
16+
2. (30%) Overwrite argv[1] to "hayaku... motohayaku!" before checking.
17+
3. (30%) Overwrite argc to 48763 before checking.
18+
You can run `validate.sh` in your local to test if you satisfy the requirements.
19+
20+
Please note that you must not alter files other than `llvm-pass.so.cc`. You will get 0 points if
21+
22+
1. you modify other files to achieve requirements.
23+
2. you can't pass all CI on your PR.
24+
25+
## Submission
26+
27+
You need to open a pull request to your branch (e.g. 311XXXXXX, your student number) and contain the code that satisfies the abovementioned requirements.
28+
29+
Moreover, please submit the URL of your PR to E3. Your submission will only be accepted when you present at both places.

lab6/ans

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
./target 1
2+
deubg mode
3+
argc = 48763
4+
argv[1] = hayaku... motohayaku!
5+
Your argc is so hayaku!
6+
Suta... basuto... sutorimu!

lab6/llvm-pass.so.cc

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#include "llvm/Passes/PassPlugin.h"
2+
#include "llvm/Passes/PassBuilder.h"
3+
#include "llvm/IR/IRBuilder.h"
4+
#include "llvm/IR/Constants.h"
5+
#include "llvm/IR/Module.h"
6+
#include "llvm/IR/Instructions.h"
7+
8+
using namespace llvm;
9+
10+
struct LLVMPass : public PassInfoMixin<LLVMPass> {
11+
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
12+
};
13+
14+
PreservedAnalyses LLVMPass::run(Module &M, ModuleAnalysisManager &MAM) {
15+
LLVMContext &Ctx = M.getContext();
16+
IntegerType *Int32Ty = IntegerType::getInt32Ty(Ctx);
17+
PointerType *Int8PtrTy = Type::getInt8PtrTy(Ctx);
18+
19+
// 宣告 debug(int) 函式
20+
FunctionType *DebugFuncTy = FunctionType::get(Type::getVoidTy(Ctx), {Int32Ty}, false);
21+
FunctionCallee debug_func = M.getOrInsertFunction("debug", DebugFuncTy);
22+
ConstantInt *debug_arg = ConstantInt::get(Int32Ty, 48763);
23+
24+
for (Function &F : M) {
25+
if (F.getName() != "main") continue;
26+
27+
// 取得 main 的參數 argc, argv
28+
Argument *argc = F.getArg(0);
29+
Argument *argv = F.getArg(1);
30+
31+
// 插入點:main 的開頭
32+
IRBuilder<> IRB(&*F.getEntryBlock().getFirstInsertionPt());
33+
34+
// 1️⃣ 呼叫 debug(48763)
35+
IRB.CreateCall(debug_func, debug_arg);
36+
37+
// 2️⃣ 將 argc 的所有用途替換為常數 48763
38+
argc->replaceAllUsesWith(debug_arg);
39+
40+
// 3️⃣ 將 argv[1] 設為 "hayaku... motohayaku!"
41+
Value *argv1_ptr = IRB.CreateGEP(argv->getType()->getPointerElementType(), argv, ConstantInt::get(Int32Ty, 1));
42+
Value *str_val = IRB.CreateGlobalStringPtr("hayaku... motohayaku!");
43+
IRB.CreateStore(str_val, argv1_ptr);
44+
}
45+
46+
return PreservedAnalyses::none();
47+
}
48+
49+
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK
50+
llvmGetPassPluginInfo() {
51+
return {LLVM_PLUGIN_API_VERSION, "LLVMPass", "1.0",
52+
[](PassBuilder &PB) {
53+
PB.registerOptimizerLastEPCallback(
54+
[](ModulePassManager &MPM, OptimizationLevel OL) {
55+
MPM.addPass(LLVMPass());
56+
});
57+
}};
58+
}

lab6/target.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
5+
void debug(int id)
6+
{
7+
if (id == 48763)
8+
printf("deubg mode\n");
9+
else
10+
printf("bad id!\n");
11+
}
12+
13+
int main(int argc, char **argv)
14+
{
15+
printf("argc = %d\n", argc);
16+
if (argc >= 2)
17+
printf("argv[1] = %s\n", argv[1]);
18+
else
19+
return 0;
20+
if (argc == 48763)
21+
printf("Your argc is so hayaku!\n");
22+
else
23+
printf("Your argc need to be modohayaku!\n");
24+
if (strcmp(argv[1], "hayaku... motohayaku!") == 0)
25+
printf("Suta... basuto... sutorimu!\n");
26+
else
27+
printf("You dead\n");
28+
return 0;
29+
}

0 commit comments

Comments
 (0)