Skip to content

Instantly share code, notes, and snippets.

@jedisct1
Created November 6, 2025 09:41
Show Gist options
  • Save jedisct1/8ea5328ff856a5350432d21f69b78153 to your computer and use it in GitHub Desktop.
Save jedisct1/8ea5328ff856a5350432d21f69b78153 to your computer and use it in GitHub Desktop.
diff --git a/lib/std/base64.zig b/lib/std/base64.zig
index 8c08fd6786..5eb9a8d283 100644
--- a/lib/std/base64.zig
+++ b/lib/std/base64.zig
@@ -313,12 +313,22 @@ pub const Base64DecoderWithIgnore = struct {
return result;
}
- /// Return the maximum possible decoded size for a given input length - The actual length may be less if the input includes padding.
- /// `InvalidPadding` is returned if the input length is not valid.
- pub fn calcSizeUpperBound(decoder_with_ignore: *const Base64DecoderWithIgnore, source_len: usize) Error!usize {
- var result = source_len / 4 * 3;
- if (decoder_with_ignore.decoder.pad_char == null) {
- const leftover = source_len % 4;
+ /// Return the maximum possible decoded size for a given input - The actual length may be less if the input includes padding.
+ /// `InvalidPadding` is returned if the input length is not valid after filtering ignored characters.
+ pub fn calcSizeUpperBound(decoder_with_ignore: *const Base64DecoderWithIgnore, source: []const u8) Error!usize {
+ var actual_len: usize = 0;
+ for (source) |c| {
+ if (!decoder_with_ignore.char_is_ignored[c]) {
+ actual_len += 1;
+ }
+ }
+
+ var result = actual_len / 4 * 3;
+ const leftover = actual_len % 4;
+ if (decoder_with_ignore.decoder.pad_char != null) {
+ if (leftover != 0) return error.InvalidPadding;
+ } else {
+ if (leftover == 1) return error.InvalidPadding;
result += leftover * 3 / 4;
}
return result;
@@ -521,7 +531,7 @@ fn testAllApis(codecs: Codecs, expected_decoded: []const u8, expected_encoded: [
{
const decoder_ignore_nothing = codecs.decoderWithIgnore("");
var buffer: [0x100]u8 = undefined;
- const decoded = buffer[0..try decoder_ignore_nothing.calcSizeUpperBound(expected_encoded.len)];
+ const decoded = buffer[0..try decoder_ignore_nothing.calcSizeUpperBound(expected_encoded)];
const written = try decoder_ignore_nothing.decode(decoded, expected_encoded);
try testing.expect(written <= decoded.len);
try testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]);
@@ -531,7 +541,7 @@ fn testAllApis(codecs: Codecs, expected_decoded: []const u8, expected_encoded: [
fn testDecodeIgnoreSpace(codecs: Codecs, expected_decoded: []const u8, encoded: []const u8) !void {
const decoder_ignore_space = codecs.decoderWithIgnore(" ");
var buffer: [0x100]u8 = undefined;
- const decoded = buffer[0..try decoder_ignore_space.calcSizeUpperBound(encoded.len)];
+ const decoded = buffer[0..try decoder_ignore_space.calcSizeUpperBound(encoded)];
const written = try decoder_ignore_space.decode(decoded, encoded);
try testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]);
}
@@ -568,3 +578,37 @@ fn testFourBytesDestNoSpaceLeftError(codecs: Codecs, encoded: []const u8) !void
return error.ExpectedError;
} else |err| if (err != error.NoSpaceLeft) return err;
}
+
+test "base64 calcSizeUpperBound with ignored chars validates padding" {
+ {
+ const decoder = standard.decoderWithIgnore(" ");
+ const input = "YWJj ZGVm Z2g="; // "abcdefgh" with spaces
+ const size = try decoder.calcSizeUpperBound(input);
+ try testing.expect(size > 0);
+ }
+
+ {
+ const decoder = standard.decoderWithIgnore(" ");
+ const invalid_input = "YWJj ZGV mZ"; // 9 chars with spaces -> 7 base64 chars, invalid for padded
+ const result = decoder.calcSizeUpperBound(invalid_input);
+ try testing.expectError(error.InvalidPadding, result);
+ }
+
+ {
+ const decoder = url_safe_no_pad.decoderWithIgnore(" ");
+ // 10 chars after filtering (valid: not 1 mod 4)
+ const input1 = "YWJj ZGVm Z2g"; // 10 base64 chars
+ _ = try decoder.calcSizeUpperBound(input1);
+
+ // 9 chars after filtering (valid: not 1 mod 4)
+ const input2 = "YWJj ZGVm Zw"; // 9 base64 chars
+ _ = try decoder.calcSizeUpperBound(input2);
+ }
+
+ {
+ const decoder = url_safe_no_pad.decoderWithIgnore(" ");
+ const input = "YWJj Z"; // 5 chars -> length 1 mod 4, always invalid
+ const result = decoder.calcSizeUpperBound(input);
+ try testing.expectError(error.InvalidPadding, result);
+ }
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment