diff --git a/glib/gbase64.c b/glib/gbase64.c index ae9d5fef55fcae8af09fce1ec38a412be6404def..dd7ed20ac86028b51b522500203b42081995fad9 100644 --- a/glib/gbase64.c +++ b/glib/gbase64.c @@ -100,12 +100,12 @@ g_base64_encode_step (const guchar *in, char *outptr; const guchar *inptr; - g_return_val_if_fail (in != NULL, 0); + g_return_val_if_fail (in != NULL || len == 0, 0); g_return_val_if_fail (out != NULL, 0); g_return_val_if_fail (state != NULL, 0); g_return_val_if_fail (save != NULL, 0); - if (len <= 0) + if (len == 0) return 0; inptr = in; @@ -160,7 +160,8 @@ g_base64_encode_step (const guchar *in, *state = already; } - if (len>0) + g_assert (len == 0 || len == 1 || len == 2); + { char *saveout; @@ -244,7 +245,7 @@ g_base64_encode_close (gboolean break_lines, /** * g_base64_encode: - * @data: (array length=len) (element-type guint8): the binary data to encode + * @data: (array length=len) (element-type guint8) (nullable): the binary data to encode * @len: the length of @data * * Encode a sequence of binary data into its Base-64 stringified @@ -268,9 +269,7 @@ g_base64_encode (const guchar *data, /* We can use a smaller limit here, since we know the saved state is 0, +1 is needed for trailing \0, also check for unlikely integer overflow */ - if (len >= ((G_MAXSIZE - 1) / 4 - 1) * 3) - g_error("%s: input too large for Base64 encoding (%"G_GSIZE_FORMAT" chars)", - G_STRLOC, len); + g_return_val_if_fail (len < ((G_MAXSIZE - 1) / 4 - 1) * 3, NULL); out = g_malloc ((len / 3 + 1) * 4 + 1); @@ -336,12 +335,12 @@ g_base64_decode_step (const gchar *in, unsigned int v; int i; - g_return_val_if_fail (in != NULL, 0); + g_return_val_if_fail (in != NULL || len == 0, 0); g_return_val_if_fail (out != NULL, 0); g_return_val_if_fail (state != NULL, 0); g_return_val_if_fail (save != NULL, 0); - if (len <= 0) + if (len == 0) return 0; inend = (const guchar *)in+len; @@ -392,7 +391,7 @@ g_base64_decode_step (const gchar *in, /** * g_base64_decode: - * @text: zero-terminated string with base64 text to decode + * @text: (not nullable): zero-terminated string with base64 text to decode * @out_len: (out): The length of the decoded data is written here * * Decode a sequence of Base-64 encoded text into binary data. Note diff --git a/glib/tests/base64.c b/glib/tests/base64.c index 86875a29b178d7cc0c73cd56e25c0fcadcd67b60..bb9d4db721085c7e2d9b56901a2134f13a3431d0 100644 --- a/glib/tests/base64.c +++ b/glib/tests/base64.c @@ -406,6 +406,85 @@ test_base64_decode_smallblock (gconstpointer blocksize_p) } } +/* Test that calling g_base64_encode (NULL, 0) returns correct output. This is + * as per the first test vector in RFC 4648 §10. + * https://tools.ietf.org/html/rfc4648#section-10 */ +static void +test_base64_encode_empty (void) +{ + gchar *encoded = NULL; + + g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/1698"); + + encoded = g_base64_encode (NULL, 0); + g_assert_cmpstr (encoded, ==, ""); + g_free (encoded); + + encoded = g_base64_encode ((const guchar *) "", 0); + g_assert_cmpstr (encoded, ==, ""); + g_free (encoded); +} + +/* Test that calling g_base64_decode ("", *) returns correct output. This is + * as per the first test vector in RFC 4648 §10. Note that calling + * g_base64_decode (NULL, *) is not allowed. + * https://tools.ietf.org/html/rfc4648#section-10 */ +static void +test_base64_decode_empty (void) +{ + guchar *decoded = NULL; + gsize decoded_len; + + g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/1698"); + + decoded = g_base64_decode ("", &decoded_len); + g_assert_cmpstr ((gchar *) decoded, ==, ""); + g_assert_cmpuint (decoded_len, ==, 0); + g_free (decoded); +} + +/* Check all the RFC 4648 test vectors for base 64 encoding from §10. + * https://tools.ietf.org/html/rfc4648#section-10 */ +static void +test_base64_encode_decode_rfc4648 (void) +{ + const struct + { + const gchar *decoded; /* technically this should be a byte array, but all the test vectors are ASCII strings */ + const gchar *encoded; + } + vectors[] = + { + { "", "" }, + { "f", "Zg==" }, + { "fo", "Zm8=" }, + { "foo", "Zm9v" }, + { "foob", "Zm9vYg==" }, + { "fooba", "Zm9vYmE=" }, + { "foobar", "Zm9vYmFy" }, + }; + gsize i; + + for (i = 0; i < G_N_ELEMENTS (vectors); i++) + { + gchar *encoded = NULL; + guchar *decoded = NULL; + gsize expected_decoded_len = strlen (vectors[i].decoded); + gsize decoded_len; + + g_test_message ("Vector %" G_GSIZE_FORMAT ": %s", i, vectors[i].decoded); + + encoded = g_base64_encode ((const guchar *) vectors[i].decoded, expected_decoded_len); + g_assert_cmpstr (encoded, ==, vectors[i].encoded); + + decoded = g_base64_decode (encoded, &decoded_len); + g_assert_cmpstr ((gchar *) decoded, ==, vectors[i].decoded); + g_assert_cmpuint (decoded_len, ==, expected_decoded_len); + + g_free (encoded); + g_free (decoded); + } +} int main (int argc, char *argv[]) @@ -455,5 +534,10 @@ main (int argc, char *argv[]) g_test_add_data_func ("/base64/incremental/smallblock/4", GINT_TO_POINTER(4), test_base64_decode_smallblock); + g_test_add_func ("/base64/encode/empty", test_base64_encode_empty); + g_test_add_func ("/base64/decode/empty", test_base64_decode_empty); + + g_test_add_func ("/base64/encode-decode/rfc4648", test_base64_encode_decode_rfc4648); + return g_test_run (); }