#include "random_data.h" #include #include #include #ifndef LLVMFUZZER static uint32_t g_random_seed = 1234; void random_set_seed(uint32_t seed) { g_random_seed = seed; } uint32_t random_get_seed() { return g_random_seed; } /* Uses xorshift64 here instead of rand() for both speed and * reproducibility across platforms. */ uint32_t rand_word() { g_random_seed ^= g_random_seed << 13; g_random_seed ^= g_random_seed >> 17; g_random_seed ^= g_random_seed << 5; return g_random_seed; } /* Get a random integer in range, with approximately flat distribution. */ int rand_int(int min, int max) { return rand_word() % (max + 1 - min) + min; } bool rand_bool() { return rand_word() & 1; } /* Get a random byte, with skewed distribution. * Important corner cases like 0xFF, 0x00 and 0xFE occur more * often than other values. */ uint8_t rand_byte() { uint32_t w = rand_word(); uint8_t b = w & 0xFF; if (w & 0x100000) b >>= (w >> 8) & 7; if (w & 0x200000) b <<= (w >> 12) & 7; if (w & 0x400000) b ^= 0xFF; return b; } /* Get a random length, with skewed distribution. * Favors the shorter lengths, but always at least 1. */ size_t rand_len(size_t max) { uint32_t w = rand_word(); size_t s; if (w & 0x800000) w &= 3; else if (w & 0x400000) w &= 15; else if (w & 0x200000) w &= 255; s = (w % max); if (s == 0) s = 1; return s; } /* Fills a buffer with random data with skewed distribution. */ void rand_fill(uint8_t *buf, size_t count) { for (; count > 0; count--) { *buf++ = rand_byte(); } } /* Fill with random protobuf-like data */ size_t rand_fill_protobuf(uint8_t *buf, size_t min_bytes, size_t max_bytes, int min_tag) { pb_ostream_t stream = pb_ostream_from_buffer(buf, max_bytes); while(stream.bytes_written < min_bytes) { pb_wire_type_t wt = rand_int(0, 3); if (wt == 3) wt = 5; /* Gap in values */ if (!pb_encode_tag(&stream, wt, rand_int(min_tag, min_tag + 512))) break; if (wt == PB_WT_VARINT) { uint64_t value; rand_fill((uint8_t*)&value, sizeof(value)); pb_encode_varint(&stream, value); } else if (wt == PB_WT_64BIT) { uint64_t value; rand_fill((uint8_t*)&value, sizeof(value)); pb_encode_fixed64(&stream, &value); } else if (wt == PB_WT_32BIT) { uint32_t value; rand_fill((uint8_t*)&value, sizeof(value)); pb_encode_fixed32(&stream, &value); } else if (wt == PB_WT_STRING) { size_t len; uint8_t *buf; if (min_bytes > stream.bytes_written) len = rand_len(min_bytes - stream.bytes_written); else len = 0; buf = malloc(len); pb_encode_varint(&stream, len); rand_fill(buf, len); pb_write(&stream, buf, len); free(buf); } } return stream.bytes_written; } /* Given a buffer of data, mess it up a bit */ void rand_mess(uint8_t *buf, size_t count) { int m = rand_int(0, 3); if (m == 0) { /* Replace random substring */ int s = rand_int(0, count - 1); int l = rand_len(count - s); rand_fill(buf + s, l); } else if (m == 1) { /* Swap random bytes */ int a = rand_int(0, count - 1); int b = rand_int(0, count - 1); int x = buf[a]; buf[a] = buf[b]; buf[b] = x; } else if (m == 2) { /* Duplicate substring */ int s = rand_int(0, count - 2); int l = rand_len((count - s) / 2); memcpy(buf + s + l, buf + s, l); } else if (m == 3) { /* Add random protobuf noise */ int s = rand_int(0, count - 1); int l = rand_len(count - s); rand_fill_protobuf(buf + s, l, count - s, 1); } } /* Append or prepend protobuf noise */ void rand_protobuf_noise(uint8_t *buffer, size_t bufsize, size_t *msglen) { int m = rand_int(0, 2); size_t max_size = bufsize - 32 - *msglen; if (m == 1) { /* Prepend */ uint8_t *tmp = malloc_with_check(bufsize); size_t s = rand_fill_protobuf(tmp, rand_len(max_size), bufsize - *msglen, 1000); memmove(buffer + s, buffer, *msglen); memcpy(buffer, tmp, s); free_with_check(tmp); *msglen += s; } else if (m == 2) { /* Append */ size_t s = rand_fill_protobuf(buffer + *msglen, rand_len(max_size), bufsize - *msglen, 1000); *msglen += s; } } #endif