1
0
mirror of https://github.com/git/git.git synced 2024-09-28 17:22:20 +02:00
git/ewah/ewah_io.c
Jeff King 9d2e330b17 ewah_read_mmap: bounds-check mmap reads
The on-disk ewah format tells us how big the ewah data is,
and we blindly read that much from the buffer without
considering whether the mmap'd data is long enough, which
can lead to out-of-bound reads.

Let's make sure we have data available before reading it,
both for the ewah header/footer as well as for the bit data
itself. In particular:

  - keep our ptr/len pair in sync as we move through the
    buffer, and check it before each read

  - check the size for integer overflow (this should be
    impossible on 64-bit, as the size is given as a 32-bit
    count of 8-byte words, but is possible on a 32-bit
    system)

  - return the number of bytes read as an ssize_t instead of
    an int, again to prevent integer overflow

  - compute the return value using a pointer difference;
    this should yield the same result as the existing code,
    but makes it more obvious that we got our computations
    right

The included test is far from comprehensive, as it just
picks a static point at which to truncate the generated
bitmap. But in practice this will hit in the middle of an
ewah and make sure we're at least exercising this code.

Reported-by: Luat Nguyen <root@l4w.io>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-06-18 09:13:57 -07:00

226 lines
5.8 KiB
C

/**
* Copyright 2013, GitHub, Inc
* Copyright 2009-2013, Daniel Lemire, Cliff Moon,
* David McIntosh, Robert Becho, Google Inc. and Veronika Zenz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "git-compat-util.h"
#include "ewok.h"
#include "strbuf.h"
int ewah_serialize_native(struct ewah_bitmap *self, int fd)
{
uint32_t write32;
size_t to_write = self->buffer_size * 8;
/* 32 bit -- bit size for the map */
write32 = (uint32_t)self->bit_size;
if (write(fd, &write32, 4) != 4)
return -1;
/** 32 bit -- number of compressed 64-bit words */
write32 = (uint32_t)self->buffer_size;
if (write(fd, &write32, 4) != 4)
return -1;
if (write(fd, self->buffer, to_write) != to_write)
return -1;
/** 32 bit -- position for the RLW */
write32 = self->rlw - self->buffer;
if (write(fd, &write32, 4) != 4)
return -1;
return (3 * 4) + to_write;
}
int ewah_serialize_to(struct ewah_bitmap *self,
int (*write_fun)(void *, const void *, size_t),
void *data)
{
size_t i;
eword_t dump[2048];
const size_t words_per_dump = sizeof(dump) / sizeof(eword_t);
uint32_t bitsize, word_count, rlw_pos;
const eword_t *buffer;
size_t words_left;
/* 32 bit -- bit size for the map */
bitsize = htonl((uint32_t)self->bit_size);
if (write_fun(data, &bitsize, 4) != 4)
return -1;
/** 32 bit -- number of compressed 64-bit words */
word_count = htonl((uint32_t)self->buffer_size);
if (write_fun(data, &word_count, 4) != 4)
return -1;
/** 64 bit x N -- compressed words */
buffer = self->buffer;
words_left = self->buffer_size;
while (words_left >= words_per_dump) {
for (i = 0; i < words_per_dump; ++i, ++buffer)
dump[i] = htonll(*buffer);
if (write_fun(data, dump, sizeof(dump)) != sizeof(dump))
return -1;
words_left -= words_per_dump;
}
if (words_left) {
for (i = 0; i < words_left; ++i, ++buffer)
dump[i] = htonll(*buffer);
if (write_fun(data, dump, words_left * 8) != words_left * 8)
return -1;
}
/** 32 bit -- position for the RLW */
rlw_pos = (uint8_t*)self->rlw - (uint8_t *)self->buffer;
rlw_pos = htonl(rlw_pos / sizeof(eword_t));
if (write_fun(data, &rlw_pos, 4) != 4)
return -1;
return (3 * 4) + (self->buffer_size * 8);
}
static int write_helper(void *fd, const void *buf, size_t len)
{
return write((intptr_t)fd, buf, len);
}
int ewah_serialize(struct ewah_bitmap *self, int fd)
{
return ewah_serialize_to(self, write_helper, (void *)(intptr_t)fd);
}
static int write_strbuf(void *user_data, const void *data, size_t len)
{
struct strbuf *sb = user_data;
strbuf_add(sb, data, len);
return len;
}
int ewah_serialize_strbuf(struct ewah_bitmap *self, struct strbuf *sb)
{
return ewah_serialize_to(self, write_strbuf, sb);
}
ssize_t ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len)
{
const uint8_t *ptr = map;
size_t data_len;
size_t i;
if (len < sizeof(uint32_t))
return error("corrupt ewah bitmap: eof before bit size");
self->bit_size = get_be32(ptr);
ptr += sizeof(uint32_t);
len -= sizeof(uint32_t);
if (len < sizeof(uint32_t))
return error("corrupt ewah bitmap: eof before length");
self->buffer_size = self->alloc_size = get_be32(ptr);
ptr += sizeof(uint32_t);
len -= sizeof(uint32_t);
REALLOC_ARRAY(self->buffer, self->alloc_size);
/*
* Copy the raw data for the bitmap as a whole chunk;
* if we're in a little-endian platform, we'll perform
* the endianness conversion in a separate pass to ensure
* we're loading 8-byte aligned words.
*/
data_len = st_mult(self->buffer_size, sizeof(eword_t));
if (len < data_len)
return error("corrupt ewah bitmap: eof in data "
"(%"PRIuMAX" bytes short)",
(uintmax_t)(data_len - len));
memcpy(self->buffer, ptr, data_len);
ptr += data_len;
len -= data_len;
for (i = 0; i < self->buffer_size; ++i)
self->buffer[i] = ntohll(self->buffer[i]);
if (len < sizeof(uint32_t))
return error("corrupt ewah bitmap: eof before rlw");
self->rlw = self->buffer + get_be32(ptr);
ptr += sizeof(uint32_t);
len -= sizeof(uint32_t);
return ptr - (const uint8_t *)map;
}
int ewah_deserialize(struct ewah_bitmap *self, int fd)
{
size_t i;
eword_t dump[2048];
const size_t words_per_dump = sizeof(dump) / sizeof(eword_t);
uint32_t bitsize, word_count, rlw_pos;
eword_t *buffer = NULL;
size_t words_left;
ewah_clear(self);
/* 32 bit -- bit size for the map */
if (read(fd, &bitsize, 4) != 4)
return -1;
self->bit_size = (size_t)ntohl(bitsize);
/** 32 bit -- number of compressed 64-bit words */
if (read(fd, &word_count, 4) != 4)
return -1;
self->buffer_size = self->alloc_size = (size_t)ntohl(word_count);
REALLOC_ARRAY(self->buffer, self->alloc_size);
/** 64 bit x N -- compressed words */
buffer = self->buffer;
words_left = self->buffer_size;
while (words_left >= words_per_dump) {
if (read(fd, dump, sizeof(dump)) != sizeof(dump))
return -1;
for (i = 0; i < words_per_dump; ++i, ++buffer)
*buffer = ntohll(dump[i]);
words_left -= words_per_dump;
}
if (words_left) {
if (read(fd, dump, words_left * 8) != words_left * 8)
return -1;
for (i = 0; i < words_left; ++i, ++buffer)
*buffer = ntohll(dump[i]);
}
/** 32 bit -- position for the RLW */
if (read(fd, &rlw_pos, 4) != 4)
return -1;
self->rlw = self->buffer + ntohl(rlw_pos);
return 0;
}