]> git.saurik.com Git - apple/libdispatch.git/blobdiff - src/transform.c
libdispatch-913.30.4.tar.gz
[apple/libdispatch.git] / src / transform.c
index 775ce41279163f89021567fa4801870598db3e3c..2c885ca36df511cf11cbcdf24c65fc35d8e72946 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Apple Inc. All rights reserved.
+ * Copyright (c) 2011-2013 Apple Inc. All rights reserved.
  *
  * @APPLE_APACHE_LICENSE_HEADER_START@
  *
 
 #include "internal.h"
 
+#ifdef __APPLE__
 #include <libkern/OSByteOrder.h>
+#elif __linux__
+#include <endian.h>
+#define OSLittleEndian __LITTLE_ENDIAN
+#define OSBigEndian __BIG_ENDIAN
+#define OSSwapLittleToHostInt16 le16toh
+#define OSSwapBigToHostInt16 be16toh
+#define OSSwapHostToLittleInt16 htole16
+#define OSSwapHostToBigInt16 htobe16
+#endif
 
 #if defined(__LITTLE_ENDIAN__)
 #define DISPATCH_DATA_FORMAT_TYPE_UTF16_HOST DISPATCH_DATA_FORMAT_TYPE_UTF16LE
@@ -28,6 +38,8 @@
 #elif defined(__BIG_ENDIAN__)
 #define DISPATCH_DATA_FORMAT_TYPE_UTF16_HOST DISPATCH_DATA_FORMAT_TYPE_UTF16BE
 #define DISPATCH_DATA_FORMAT_TYPE_UTF16_REV DISPATCH_DATA_FORMAT_TYPE_UTF16LE
+#else
+#error Unsupported Endianness
 #endif
 
 enum {
@@ -37,14 +49,15 @@ enum {
        _DISPATCH_DATA_FORMAT_UTF16BE = 0x8,
        _DISPATCH_DATA_FORMAT_UTF_ANY = 0x10,
        _DISPATCH_DATA_FORMAT_BASE32 = 0x20,
-       _DISPATCH_DATA_FORMAT_BASE64 = 0x40,
+       _DISPATCH_DATA_FORMAT_BASE32HEX = 0x40,
+       _DISPATCH_DATA_FORMAT_BASE64 = 0x80,
 };
 
 #pragma mark -
 #pragma mark baseXX tables
 
-static const char base32_encode_table[] =
-               "ABCDEFGHIJKLMNOPQRSTUVWXYZ23456789";
+static const unsigned char base32_encode_table[] =
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
 
 static const char base32_decode_table[] = {
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
@@ -57,7 +70,21 @@ static const char base32_decode_table[] = {
 static const ssize_t base32_decode_table_size = sizeof(base32_decode_table)
                / sizeof(*base32_decode_table);
 
-static const char base64_encode_table[] =
+static const unsigned char base32hex_encode_table[] =
+               "0123456789ABCDEFGHIJKLMNOPQRSTUV";
+
+static const char base32hex_decode_table[] = {
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  0,  1,  2,
+        3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -2, -1, -1, -1, 10, 11, 12,
+       13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+       30, 31
+};
+static const ssize_t base32hex_decode_table_size =
+               sizeof(base32hex_encode_table) / sizeof(*base32hex_encode_table);
+
+static const unsigned char base64_encode_table[] =
                "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 static const char base64_decode_table[] = {
@@ -88,28 +115,18 @@ typedef struct dispatch_transform_buffer_s {
        size_t size;
 } dispatch_transform_buffer_s;
 
-static size_t
-_dispatch_transform_sizet_mul(size_t a, size_t b)
-{
-       size_t rv = SIZE_MAX;
-       if (a == 0 || rv/a >= b) {
-               rv = a * b;
-       }
-       return rv;
-}
-
 #define BUFFER_MALLOC_MAX (100*1024*1024)
 
 static bool
 _dispatch_transform_buffer_new(dispatch_transform_buffer_s *buffer,
                size_t required, size_t size)
 {
-       size_t remaining = buffer->size - (buffer->ptr.u8 - buffer->start);
+       size_t remaining = buffer->size - (size_t)(buffer->ptr.u8 - buffer->start);
        if (required == 0 || remaining < required) {
                if (buffer->start) {
                        if (buffer->ptr.u8 > buffer->start) {
                                dispatch_data_t _new = dispatch_data_create(buffer->start,
-                                               buffer->ptr.u8 - buffer->start, NULL,
+                                               (size_t)(buffer->ptr.u8 - buffer->start), NULL,
                                                DISPATCH_DATA_DESTRUCTOR_FREE);
                                dispatch_data_t _concat = dispatch_data_create_concat(
                                                buffer->data, _new);
@@ -271,11 +288,13 @@ _dispatch_transform_to_utf16(dispatch_data_t data, int32_t byteOrder)
                        DISPATCH_UNUSED dispatch_data_t region,
                        size_t offset, const void *_buffer, size_t size) {
                const uint8_t *src = _buffer;
-               size_t i;
+               size_t i, dest_size;
 
                if (offset == 0) {
-                       size_t dest_size = 2 + _dispatch_transform_sizet_mul(size,
-                                       sizeof(uint16_t));
+                       if (os_mul_and_add_overflow(size, sizeof(uint16_t),
+                                       sizeof(uint16_t), &dest_size)) {
+                               return (bool)false;
+                       }
                        if (!_dispatch_transform_buffer_new(&buffer, dest_size, 0)) {
                                return (bool)false;
                        }
@@ -297,6 +316,7 @@ _dispatch_transform_to_utf16(dispatch_data_t data, int32_t byteOrder)
                for (i = 0; i < size;) {
                        uint32_t wch = 0;
                        uint8_t byte_size = _dispatch_transform_utf8_length(*src);
+                       size_t next;
 
                        if (byte_size == 0) {
                                return (bool)false;
@@ -321,7 +341,9 @@ _dispatch_transform_to_utf16(dispatch_data_t data, int32_t byteOrder)
                                i += byte_size;
                        }
 
-                       size_t next = _dispatch_transform_sizet_mul(size - i, sizeof(uint16_t));
+                       if (os_mul_overflow(size - i, sizeof(uint16_t), &next)) {
+                               return (bool)false;
+                       }
                        if (wch >= 0xd800 && wch < 0xdfff) {
                                // Illegal range (surrogate pair)
                                return (bool)false;
@@ -352,6 +374,7 @@ _dispatch_transform_to_utf16(dispatch_data_t data, int32_t byteOrder)
        });
 
        if (!success) {
+               (void)_dispatch_transform_buffer_new(&buffer, 0, 0);
                dispatch_release(buffer.data);
                return NULL;
        }
@@ -374,8 +397,8 @@ _dispatch_transform_from_utf16(dispatch_data_t data, int32_t byteOrder)
                const uint16_t *src = _buffer;
 
                if (offset == 0) {
+                       size_t dest_size = howmany(size, 3) * 2;
                        // Assume first buffer will be mostly single-byte UTF-8 sequences
-                       size_t dest_size = _dispatch_transform_sizet_mul(size, 2) / 3;
                        if (!_dispatch_transform_buffer_new(&buffer, dest_size, 0)) {
                                return (bool)false;
                        }
@@ -402,6 +425,7 @@ _dispatch_transform_from_utf16(dispatch_data_t data, int32_t byteOrder)
                for (i = 0; i < max; i++) {
                        uint32_t wch = 0;
                        uint16_t ch;
+                       size_t next;
 
                        if ((i == (max - 1)) && (max > (size / 2))) {
                                // Last byte of an odd sized range
@@ -411,7 +435,8 @@ _dispatch_transform_from_utf16(dispatch_data_t data, int32_t byteOrder)
                                if (range == NULL) {
                                        return (bool)false;
                                }
-                               ch = _dispatch_transform_swap_to_host(*(uint64_t*)p, byteOrder);
+                               ch = _dispatch_transform_swap_to_host((uint16_t)*(uint64_t*)p,
+                                               byteOrder);
                                dispatch_release(range);
                                skip += 1;
                        } else {
@@ -428,7 +453,7 @@ _dispatch_transform_from_utf16(dispatch_data_t data, int32_t byteOrder)
 
                        if ((ch >= 0xd800) && (ch <= 0xdbff)) {
                                // Surrogate pair
-                               wch = ((ch - 0xd800) << 10);
+                               wch = ((ch - 0xd800u) << 10);
                                if (++i >= max) {
                                        // Surrogate byte isn't in this block
                                        const void *p;
@@ -455,7 +480,9 @@ _dispatch_transform_from_utf16(dispatch_data_t data, int32_t byteOrder)
                                wch = ch;
                        }
 
-                       size_t next = _dispatch_transform_sizet_mul(max - i, 2);
+                       if (os_mul_overflow(max - i, 2, &next)) {
+                               return (bool)false;
+                       }
                        if (wch < 0x80) {
                                if (!_dispatch_transform_buffer_new(&buffer, 1, next)) {
                                        return (bool)false;
@@ -491,6 +518,7 @@ _dispatch_transform_from_utf16(dispatch_data_t data, int32_t byteOrder)
        });
 
        if (!success) {
+               (void)_dispatch_transform_buffer_new(&buffer, 0, 0);
                dispatch_release(buffer.data);
                return NULL;
        }
@@ -526,7 +554,8 @@ _dispatch_transform_to_utf16be(dispatch_data_t data)
 #pragma mark base32
 
 static dispatch_data_t
-_dispatch_transform_from_base32(dispatch_data_t data)
+_dispatch_transform_from_base32_with_table(dispatch_data_t data,
+               const char* table, ssize_t table_size)
 {
        __block uint64_t x = 0, count = 0, pad = 0;
 
@@ -535,9 +564,8 @@ _dispatch_transform_from_base32(dispatch_data_t data)
        bool success = dispatch_data_apply(data, ^(
                        DISPATCH_UNUSED dispatch_data_t region,
                        DISPATCH_UNUSED size_t offset, const void *buffer, size_t size) {
-               size_t i, dest_size = (size * 5) / 8;
-
-               uint8_t *dest = (uint8_t*)malloc(dest_size * sizeof(char));
+               size_t i, dest_size = howmany(size, 8) * 5;
+               uint8_t *dest = (uint8_t*)malloc(dest_size * sizeof(uint8_t));
                uint8_t *ptr = dest;
                if (dest == NULL) {
                        return (bool)false;
@@ -551,21 +579,20 @@ _dispatch_transform_from_base32(dispatch_data_t data)
                        }
 
                        ssize_t index = bytes[i];
-                       if (index >= base32_decode_table_size ||
-                                       base32_decode_table[index] == -1) {
+                       if (index >= table_size || table[index] == -1) {
                                free(dest);
                                return (bool)false;
                        }
                        count++;
 
-                       char value = base32_decode_table[index];
+                       char value = table[index];
                        if (value == -2) {
                                value = 0;
                                pad++;
                        }
 
                        x <<= 5;
-                       x += value;
+                       x += (uint64_t)value;
 
                        if ((count & 0x7) == 0) {
                                *ptr++ = (x >> 32) & 0xff;
@@ -576,7 +603,7 @@ _dispatch_transform_from_base32(dispatch_data_t data)
                        }
                }
 
-               size_t final = (ptr - dest);
+               size_t final = (size_t)(ptr - dest);
                switch (pad) {
                case 1:
                        final -= 1;
@@ -612,15 +639,20 @@ _dispatch_transform_from_base32(dispatch_data_t data)
 }
 
 static dispatch_data_t
-_dispatch_transform_to_base32(dispatch_data_t data)
+_dispatch_transform_to_base32_with_table(dispatch_data_t data, const unsigned char* table)
 {
-       size_t total = dispatch_data_get_size(data);
+       size_t total = dispatch_data_get_size(data), dest_size;
        __block size_t count = 0;
 
-       size_t dest_size = ((total + 4) * 8) / 5;
-       dest_size -= dest_size % 8;
+       dest_size = howmany(total, 5);
+       // <rdar://problem/25676583>
+       // os_mul_overflow(dest_size, 8, &dest_size)
+       if (dest_size > SIZE_T_MAX / 8) {
+               return NULL;
+       }
+       dest_size *= 8;
 
-       uint8_t *dest = (uint8_t*)malloc(dest_size * sizeof(uint8_t));
+       uint8_t *dest = (uint8_t*)malloc(dest_size);
        if (dest == NULL) {
                return NULL;
        }
@@ -660,26 +692,26 @@ _dispatch_transform_to_base32(dispatch_data_t data)
                        switch (count % 5) {
                        case 0:
                                // a
-                               *ptr++ = base32_encode_table[(curr >> 3) & 0x1f];
+                               *ptr++ = table[(curr >> 3) & 0x1fu];
                                break;
                        case 1:
                                // b + c
-                               *ptr++ = base32_encode_table[((last << 2)|(curr >> 6)) & 0x1f];
-                               *ptr++ = base32_encode_table[(curr >> 1) & 0x1f];
+                               *ptr++ = table[((last << 2)|(curr >> 6)) & 0x1f];
+                               *ptr++ = table[(curr >> 1) & 0x1f];
                                break;
                        case 2:
                                // d
-                               *ptr++ = base32_encode_table[((last << 4)|(curr >> 4)) & 0x1f];
+                               *ptr++ = table[((last << 4)|(curr >> 4)) & 0x1f];
                                break;
                        case 3:
                                // e + f
-                               *ptr++ = base32_encode_table[((last << 1)|(curr >> 7)) & 0x1f];
-                               *ptr++ = base32_encode_table[(curr >> 2) & 0x1f];
+                               *ptr++ = table[((last << 1)|(curr >> 7)) & 0x1f];
+                               *ptr++ = table[(curr >> 2) & 0x1f];
                                break;
                        case 4:
                                // g + h
-                               *ptr++ = base32_encode_table[((last << 3)|(curr >> 5)) & 0x1f];
-                               *ptr++ = base32_encode_table[curr & 0x1f];
+                               *ptr++ = table[((last << 3)|(curr >> 5)) & 0x1f];
+                               *ptr++ = table[curr & 0x1f];
                                break;
                        }
                }
@@ -691,19 +723,19 @@ _dispatch_transform_to_base32(dispatch_data_t data)
                                break;
                        case 1:
                                // b[4:2]
-                               *ptr++ = base32_encode_table[(bytes[size-1] << 2) & 0x1c];
+                               *ptr++ = table[(bytes[size-1] << 2) & 0x1c];
                                break;
                        case 2:
                                // d[4]
-                               *ptr++ = base32_encode_table[(bytes[size-1] << 4) & 0x10];
+                               *ptr++ = table[(bytes[size-1] << 4) & 0x10];
                                break;
                        case 3:
                                // e[4:1]
-                               *ptr++ = base32_encode_table[(bytes[size-1] << 1) & 0x1e];
+                               *ptr++ = table[(bytes[size-1] << 1) & 0x1e];
                                break;
                        case 4:
-                               // g[4:3]
-                               *ptr++ = base32_encode_table[bytes[size-1] & 0x18];
+                               // g[2:3]
+                               *ptr++ = table[(bytes[size-1] << 3) & 0x18];
                                break;
                        }
                        switch (count % 5) {
@@ -734,6 +766,33 @@ _dispatch_transform_to_base32(dispatch_data_t data)
                        DISPATCH_DATA_DESTRUCTOR_FREE);
 }
 
+static dispatch_data_t
+_dispatch_transform_from_base32(dispatch_data_t data)
+{
+       return _dispatch_transform_from_base32_with_table(data, base32_decode_table,
+                       base32_decode_table_size);
+}
+
+static dispatch_data_t
+_dispatch_transform_to_base32(dispatch_data_t data)
+{
+       return _dispatch_transform_to_base32_with_table(data, base32_encode_table);
+}
+
+static dispatch_data_t
+_dispatch_transform_from_base32hex(dispatch_data_t data)
+{
+       return _dispatch_transform_from_base32_with_table(data,
+                       base32hex_decode_table, base32hex_decode_table_size);
+}
+
+static dispatch_data_t
+_dispatch_transform_to_base32hex(dispatch_data_t data)
+{
+       return _dispatch_transform_to_base32_with_table(data,
+                       base32hex_encode_table);
+}
+
 #pragma mark -
 #pragma mark base64
 
@@ -748,7 +807,7 @@ _dispatch_transform_from_base64(dispatch_data_t data)
        bool success = dispatch_data_apply(data, ^(
                        DISPATCH_UNUSED dispatch_data_t region,
                        DISPATCH_UNUSED size_t offset, const void *buffer, size_t size) {
-               size_t i, dest_size = (size * 3) / 4;
+               size_t i, dest_size = howmany(size, 4) * 3;
 
                uint8_t *dest = (uint8_t*)malloc(dest_size * sizeof(uint8_t));
                uint8_t *ptr = dest;
@@ -778,7 +837,7 @@ _dispatch_transform_from_base64(dispatch_data_t data)
                        }
 
                        x <<= 6;
-                       x += value;
+                       x += (uint64_t)value;
 
                        if ((count & 0x3) == 0) {
                                *ptr++ = (x >> 16) & 0xff;
@@ -787,7 +846,7 @@ _dispatch_transform_from_base64(dispatch_data_t data)
                        }
                }
 
-               size_t final = (ptr - dest);
+               size_t final = (size_t)(ptr - dest);
                if (pad > 0) {
                        // 2 bytes of pad means only had one char in final group
                        final -= pad;
@@ -817,13 +876,18 @@ _dispatch_transform_to_base64(dispatch_data_t data)
 {
        // RFC 4648 states that we should not linebreak
        // http://tools.ietf.org/html/rfc4648
-       size_t total = dispatch_data_get_size(data);
+       size_t total = dispatch_data_get_size(data), dest_size;
        __block size_t count = 0;
 
-       size_t dest_size = ((total + 2) * 4) / 3;
-       dest_size -= dest_size % 4;
+       dest_size = howmany(total, 3);
+       // <rdar://problem/25676583>
+       // os_mul_overflow(dest_size, 4, &dest_size)
+       if (dest_size > SIZE_T_MAX / 4) {
+               return NULL;
+       }
+       dest_size *= 4;
 
-       uint8_t *dest = (uint8_t*)malloc(dest_size * sizeof(uint8_t));
+       uint8_t *dest = (uint8_t*)malloc(dest_size);
        if (dest == NULL) {
                return NULL;
        }
@@ -910,14 +974,17 @@ dispatch_data_create_with_transform(dispatch_data_t data,
 {
        if (input->type == _DISPATCH_DATA_FORMAT_UTF_ANY) {
                input = _dispatch_transform_detect_utf(data);
+               if (input == NULL) {
+                       return DISPATCH_BAD_INPUT;
+               }
        }
 
        if ((input->type & ~output->input_mask) != 0) {
-               return NULL;
+               return DISPATCH_BAD_INPUT;
        }
 
        if ((output->type & ~input->output_mask) != 0) {
-               return NULL;
+               return DISPATCH_BAD_INPUT;
        }
 
        if (dispatch_data_get_size(data) == 0) {
@@ -933,7 +1000,7 @@ dispatch_data_create_with_transform(dispatch_data_t data,
        }
 
        if (!temp1) {
-               return NULL;
+               return DISPATCH_BAD_INPUT;
        }
 
        dispatch_data_t temp2;
@@ -950,8 +1017,8 @@ dispatch_data_create_with_transform(dispatch_data_t data,
 
 const struct dispatch_data_format_type_s _dispatch_data_format_type_none = {
        .type = _DISPATCH_DATA_FORMAT_NONE,
-       .input_mask = ~0,
-       .output_mask = ~0,
+       .input_mask = ~0u,
+       .output_mask = ~0u,
        .decode = NULL,
        .encode = NULL,
 };
@@ -959,19 +1026,30 @@ const struct dispatch_data_format_type_s _dispatch_data_format_type_none = {
 const struct dispatch_data_format_type_s _dispatch_data_format_type_base32 = {
        .type = _DISPATCH_DATA_FORMAT_BASE32,
        .input_mask = (_DISPATCH_DATA_FORMAT_NONE | _DISPATCH_DATA_FORMAT_BASE32 |
-                       _DISPATCH_DATA_FORMAT_BASE64),
+                       _DISPATCH_DATA_FORMAT_BASE32HEX | _DISPATCH_DATA_FORMAT_BASE64),
        .output_mask = (_DISPATCH_DATA_FORMAT_NONE | _DISPATCH_DATA_FORMAT_BASE32 |
-                       _DISPATCH_DATA_FORMAT_BASE64),
+                       _DISPATCH_DATA_FORMAT_BASE32HEX | _DISPATCH_DATA_FORMAT_BASE64),
        .decode = _dispatch_transform_from_base32,
        .encode = _dispatch_transform_to_base32,
 };
 
+const struct dispatch_data_format_type_s _dispatch_data_format_type_base32hex =
+{
+       .type = _DISPATCH_DATA_FORMAT_BASE32HEX,
+       .input_mask = (_DISPATCH_DATA_FORMAT_NONE | _DISPATCH_DATA_FORMAT_BASE32 |
+                       _DISPATCH_DATA_FORMAT_BASE32HEX | _DISPATCH_DATA_FORMAT_BASE64),
+       .output_mask = (_DISPATCH_DATA_FORMAT_NONE | _DISPATCH_DATA_FORMAT_BASE32 |
+                       _DISPATCH_DATA_FORMAT_BASE32HEX | _DISPATCH_DATA_FORMAT_BASE64),
+       .decode = _dispatch_transform_from_base32hex,
+       .encode = _dispatch_transform_to_base32hex,
+};
+
 const struct dispatch_data_format_type_s _dispatch_data_format_type_base64 = {
        .type = _DISPATCH_DATA_FORMAT_BASE64,
        .input_mask = (_DISPATCH_DATA_FORMAT_NONE | _DISPATCH_DATA_FORMAT_BASE32 |
-                       _DISPATCH_DATA_FORMAT_BASE64),
+                       _DISPATCH_DATA_FORMAT_BASE32HEX | _DISPATCH_DATA_FORMAT_BASE64),
        .output_mask = (_DISPATCH_DATA_FORMAT_NONE | _DISPATCH_DATA_FORMAT_BASE32 |
-                       _DISPATCH_DATA_FORMAT_BASE64),
+                       _DISPATCH_DATA_FORMAT_BASE32HEX | _DISPATCH_DATA_FORMAT_BASE64),
        .decode = _dispatch_transform_from_base64,
        .encode = _dispatch_transform_to_base64,
 };