diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-19 22:48:04 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-19 22:48:04 +0000 |
| commit | 23065ddf6ed0b3762dda5f5059888eb52b5c2415 (patch) | |
| tree | aec187b8211203081e8dacb07a5ce325eb348cc4 | |
| parent | 3af5dcf34d705cc52c1ffe5b85c2a90b5104e4c9 (diff) | |
Fixes related to memory safety. I will work more on this related to the stdlib.
| -rw-r--r-- | docs/std/vec.md | 1 | ||||
| -rw-r--r-- | std/fs.zc | 18 | ||||
| -rw-r--r-- | std/option.zc | 16 | ||||
| -rw-r--r-- | std/path.zc | 4 | ||||
| -rw-r--r-- | std/result.zc | 8 | ||||
| -rw-r--r-- | std/string.zc | 34 | ||||
| -rw-r--r-- | std/vec.zc | 7 | ||||
| -rw-r--r-- | tests/collections/test_string_suite.zc | 12 | ||||
| -rw-r--r-- | tests/std/test_fs.zc | 13 |
9 files changed, 83 insertions, 30 deletions
diff --git a/docs/std/vec.md b/docs/std/vec.md index a0422bb..bda8d5b 100644 --- a/docs/std/vec.md +++ b/docs/std/vec.md @@ -87,4 +87,5 @@ struct Vec<T> { | Method | Signature | Description | | :--- | :--- | :--- | | **Free** | `free(self)` | Manually frees memory. Safe to call multiple times. | +| **Forget** | `forget(self)` | Detaches the memory buffer from the vector (sets fields to 0). Prevents `Drop` from freeing memory. Useful for implementing move semantics or transferring ownership. | | **Trait** | `impl Drop for Vec` | Automatically calls `free()` when `Vec` goes out of scope. | @@ -50,7 +50,7 @@ struct Metadata { is_file: bool; } -@derive(Eq) +// @derive(Eq) - Removed to avoid double-free on String field struct DirEntry { name: String; is_dir: bool; @@ -87,7 +87,11 @@ impl File { var s = String::new(buffer); free(buffer); - return Result<String>::Ok(s); + var res = Result<String>::Ok(s); + s.forget(); + var ret = res; + res.forget(); + return ret; } fn read_all(path: char*) -> Result<String> { @@ -97,7 +101,9 @@ impl File { var f: File = res.unwrap(); var s_res = f.read_to_string(); f.close(); - return s_res; + var ret = s_res; + s_res.forget(); + return ret; } fn write_string(self, content: char*) -> Result<bool> { @@ -157,6 +163,10 @@ impl File { free(name_buf); closedir(dir); - return Result< Vec<DirEntry> >::Ok(entries); + var res = Result< Vec<DirEntry> >::Ok(entries); + entries.forget(); + var ret = res; + res.forget(); + return ret; } } diff --git a/std/option.zc b/std/option.zc index 5652bd7..11b4428 100644 --- a/std/option.zc +++ b/std/option.zc @@ -23,12 +23,26 @@ impl Option<T> { return !self.is_some; } + fn forget(self) { + memset(&self.val, 0, sizeof(T)); + } + fn unwrap(self) -> T { if (!self.is_some) { !"Panic: unwrap called on None"; exit(1); } - return self.val; + var v = self.val; + memset(&self.val, 0, sizeof(T)); + return v; + } + + fn unwrap_ref(self) -> T* { + if (!self.is_some) { + !"Panic: unwrap_ref called on None"; + exit(1); + } + return &self.val; } fn unwrap_or(self, def_val: T) -> T { diff --git a/std/path.zc b/std/path.zc index 6a9531d..7055880 100644 --- a/std/path.zc +++ b/std/path.zc @@ -33,12 +33,12 @@ impl Path { var last = self.str.vec.get(base_len - 1); if (last != '/' && last != '\\') { var sep = String::new("/"); - new_s.append(sep); + new_s.append(&sep); } } var other_str = String::new(other); - new_s.append(other_str); + new_s.append(&other_str); return Path { str: new_s }; } diff --git a/std/result.zc b/std/result.zc index b043162..3794f1a 100644 --- a/std/result.zc +++ b/std/result.zc @@ -24,12 +24,18 @@ impl Result<T> { return !self.is_ok; } + fn forget(self) { + memset(&self.val, 0, sizeof(T)); + } + fn unwrap(self) -> T { if (!self.is_ok) { !"Panic: unwrap called on Err: {self.err}"; exit(1); } - return self.val; + var v = self.val; + memset(&self.val, 0, sizeof(T)); + return v; } fn expect(self, msg: char*) -> T { diff --git a/std/string.zc b/std/string.zc index 4e0ba03..2e59179 100644 --- a/std/string.zc +++ b/std/string.zc @@ -16,7 +16,11 @@ impl String { v.push(s[i]); } v.push(0); - return String { vec: v }; + var d = v.data; + var l = v.len; + var c = v.cap; + v.forget(); + return String { vec: Vec<char> { data: d, len: l, cap: c } }; } fn from(s: char*) -> String { @@ -30,23 +34,31 @@ impl String { fn destroy(self) { self.vec.free(); } + + fn forget(self) { + self.vec.forget(); + } - fn append(self, other: String) { + fn append(self, other: String*) { self.vec.len = self.vec.len - 1; - var other_len = other.vec.len; + var other_len = (*other).vec.len; for (var i = 0; i < other_len; i = i + 1) { - self.vec.push(other.vec.get(i)); + self.vec.push((*other).vec.get(i)); } } - fn add(self, other: String) -> String { + fn add(self, other: String*) -> String { var new_s = String::from(self.c_str()); new_s.append(other); - return new_s; + var d = new_s.vec.data; + var l = new_s.vec.len; + var c = new_s.vec.cap; + new_s.forget(); + return String { vec: Vec<char> { data: d, len: l, cap: c } }; } - fn eq(self, other: String) -> bool { - return strcmp(self.c_str(), other.c_str()) == 0; + fn eq(self, other: String*) -> bool { + return strcmp(self.c_str(), (*other).c_str()) == 0; } fn length(self) -> usize { @@ -66,7 +78,11 @@ impl String { v.push(self.vec.get(start + i)); } v.push(0); - return String { vec: v }; + var d = v.data; + var l = v.len; + var c = v.cap; + v.forget(); + return String { vec: Vec<char> { data: d, len: l, cap: c } }; } fn find(self, target: char) -> Option<usize> { @@ -244,6 +244,13 @@ impl Vec<T> { return true; } + // Prevent Drop from freeing memory (simulates move) + fn forget(self) { + self.data = 0; + self.len = 0; + self.cap = 0; + } + fn clone(self) -> Vec<T> { if (self.len == 0) { return Vec<T> { data: 0, len: 0, cap: 0 }; diff --git a/tests/collections/test_string_suite.zc b/tests/collections/test_string_suite.zc index 4d2c080..a3f608d 100644 --- a/tests/collections/test_string_suite.zc +++ b/tests/collections/test_string_suite.zc @@ -10,7 +10,7 @@ test "test_string_methods" { var s1: String = String::from("Hello World"); var sub: String = s1.substring(0, 5); var expected1: String = String::from("Hello"); - if (sub.eq(expected1)) { + if (sub.eq(&expected1)) { println " -> substring(0, 5): Passed"; } else { assert(false, "substring(0, 5) failed"); @@ -19,7 +19,7 @@ test "test_string_methods" { // Substring middle var sub2: String = s1.substring(6, 5); var expected2: String = String::from("World"); - if (sub2.eq(expected2)) { + if (sub2.eq(&expected2)) { println " -> substring(6, 5): Passed"; } else { assert(false, "substring(6, 5) failed"); @@ -67,9 +67,9 @@ test "test_string_methods" { // Append var s2: String = String::from("Hello"); var s3: String = String::from(" World"); - s2.append(s3); + s2.append(&s3); var expected6: String = String::from("Hello World"); - if (s2.eq(expected6)) { + if (s2.eq(&expected6)) { println " -> append(): Passed"; } else { assert(false, "append() failed"); @@ -99,7 +99,7 @@ test "test_string_std_ops" { var s1 = String::from("Hello"); var s2 = String::from(" World"); - var s3 = s1 + s2; + var s3 = s1.add(&s2); print "Concatenated: "; s3.println(); @@ -107,7 +107,7 @@ test "test_string_std_ops" { assert(s3.length() == 11, "Length mismatch"); var s4 = String::from("Hello World"); - if (s3.eq(s4)) { + if (s3.eq(&s4)) { println "Equality check passed: Strings are identical."; } else { assert(false, "Equality check failed"); diff --git a/tests/std/test_fs.zc b/tests/std/test_fs.zc index 7f66589..fc6d069 100644 --- a/tests/std/test_fs.zc +++ b/tests/std/test_fs.zc @@ -13,19 +13,19 @@ test "test_std_fs_extended" { var p3 = Path::new("file.txt"); var ext = p3.extension(); assert(ext.is_some(), "Extension missed"); - assert(strcmp(ext.unwrap().c_str(), ".txt") == 0, "Wrong extension"); + assert(strcmp(ext.unwrap_ref().c_str(), ".txt") == 0, "Wrong extension"); var p4 = Path::new("/usr/bin/gcc"); var parent = p4.parent(); assert(parent.is_some(), "Parent missed"); - assert(strcmp(parent.unwrap().c_str(), "/usr/bin") == 0, "Wrong parent"); + assert(strcmp(parent.unwrap_ref().c_str(), "/usr/bin") == 0, "Wrong parent"); var fname = p4.file_name(); assert(fname.is_some(), "Filename missed"); - assert(strcmp(fname.unwrap().c_str(), "gcc") == 0, "Wrong filename"); + assert(strcmp(fname.unwrap_ref().c_str(), "gcc") == 0, "Wrong filename"); "Testing FS..."; - var test_dir = "test_fs_sandbox"; + var test_dir = "tests/test_fs_sandbox"; if (File::exists(test_dir)) { File::remove_dir(test_dir); @@ -63,9 +63,8 @@ test "test_std_fs_extended" { assert(list_res.is_ok(), "Read dir failed"); var entries = list_res.unwrap(); var found_idx = -1; - for (var i: usize = 0; i < entries.length(); i = i + 1) { - var entry: DirEntry = entries.get(i); - if (strcmp(entry.name.c_str(), "hello.txt") == 0) { + for entry in &entries { + if (strcmp((*entry).name.c_str(), "hello.txt") == 0) { found_idx = 1; break; } |
