aboutsummaryrefslogtreecommitdiff
path: root/core/encoding/base32
diff options
context:
space:
mode:
authorZoltán Kéri <z@zolk3ri.name>2024-12-24 02:20:32 +0100
committerZoltán Kéri <z@zolk3ri.name>2024-12-24 02:20:32 +0100
commitb9338777e34006b40b9315e795232e0608caa499 (patch)
tree625a820424a3309825f73fd9e7dabadec0c0247e /core/encoding/base32
parent8c761627c84847bae3ec5e77a1408c542d9460b7 (diff)
encoding/base32: Fix buffer allocation and bounds checking
Fix buffer allocation size calculation and add proper bounds checking to ensure output buffer has sufficient space. This fixes crashes that could occur with inputs like "AA" and other edge cases where the output buffer was too small. Remove #no_bounds_check as proper bounds checking is necessary for safe error handling. The small performance trade-off is worth the improved robustness.
Diffstat (limited to 'core/encoding/base32')
-rw-r--r--core/encoding/base32/base32.odin30
1 files changed, 24 insertions, 6 deletions
diff --git a/core/encoding/base32/base32.odin b/core/encoding/base32/base32.odin
index 688b27544..8e3499dce 100644
--- a/core/encoding/base32/base32.odin
+++ b/core/encoding/base32/base32.odin
@@ -100,15 +100,18 @@ _encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.al
}
}
-decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> ([]byte, Error) #no_bounds_check {
+decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> ([]byte, Error) {
if len(data) == 0 {
return nil, .None
}
+ // Calculate maximum possible output size and allocate buffer
+ out_len := (len(data) * 5 + 7) / 8 // Ceiling division to ensure enough space
+ out := make([]byte, out_len, allocator)
+
outi := 0
data := data
- out := make([]byte, len(data) / 8 * 5, allocator)
end := false
for len(data) > 0 && !end {
dbuf : [8]byte
@@ -122,25 +125,22 @@ decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocato
input := data[0]
data = data[1:]
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
- // assert(!(len(data) + j < 8 - 1), "Corrupted input")
if len(data) + j < 8 - 1 {
return nil, .Malformed_Input
}
- // assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input")
for k := 0; k < 8-1-j; k += 1 {
if len(data) < k || data[k] != byte(PADDING) {
return nil, .Malformed_Input
}
}
dlen, end = j, true
- // assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input")
if dlen == 1 || dlen == 3 || dlen == 6 {
return nil, .Invalid_Length
}
break
}
+
decoded := DEC_TBL[input]
- // assert(dbuf[j] != 0xff, "Corrupted input")
if decoded == 0 && input != byte(ENC_TABLE[0]) {
return nil, .Invalid_Character
}
@@ -148,23 +148,41 @@ decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocato
j += 1
}
+ // Ensure we have enough space in output buffer
+ needed := 5 // Each full 8-char block produces 5 bytes
+ if outi + needed > len(out) {
+ return nil, .Invalid_Length
+ }
+
+ // Process complete input blocks
switch dlen {
case 8:
+ if len(dbuf) < 8 { return nil, .Invalid_Length }
out[outi + 4] = dbuf[6] << 5 | dbuf[7]
fallthrough
case 7:
+ if len(dbuf) < 7 { return nil, .Invalid_Length }
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3
fallthrough
case 5:
+ if len(dbuf) < 5 { return nil, .Invalid_Length }
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1
fallthrough
case 4:
+ if len(dbuf) < 4 { return nil, .Invalid_Length }
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4
fallthrough
case 2:
+ if len(dbuf) < 2 { return nil, .Invalid_Length }
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2
}
outi += 5
}
+
+ // Trim output buffer to actual size
+ if outi < len(out) {
+ out = out[:outi]
+ }
+
return out, .None
}