dir: support platforms that require aligned reads

The untracked cache is stored on disk by concatenating its memory
structures without any padding.  Consequently some of the structs are
not aligned at a particular boundary when the whole extension is read
back in one go.  That's only OK on platforms without strict alignment
requirements, or for byte-aligned data like strings or hash values.

Decode struct ondisk_untracked_cache carefully from the extension
blob by using explicit pointer arithmetic with offsets, avoiding
alignment issues.  Use char pointers for passing stat_data objects to
stat_data_from_disk(), and use memcpy(3) in that function to  get the
contents into a properly aligned struct, then perform the byte-order
adjustment in place there.

Found with Clang's UBSan.

Signed-off-by: Rene Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
René Scharfe 2017-07-16 14:17:37 +02:00 committed by Junio C Hamano
parent 95d6787973
commit 268ba20110
1 changed files with 27 additions and 23 deletions

50
dir.c
View File

@ -2290,7 +2290,8 @@ struct ondisk_untracked_cache {
char exclude_per_dir[FLEX_ARRAY]; char exclude_per_dir[FLEX_ARRAY];
}; };


#define ouc_size(len) (offsetof(struct ondisk_untracked_cache, exclude_per_dir) + len + 1) #define ouc_offset(x) offsetof(struct ondisk_untracked_cache, x)
#define ouc_size(len) (ouc_offset(exclude_per_dir) + len + 1)


struct write_data { struct write_data {
int index; /* number of written untracked_cache_dir */ int index; /* number of written untracked_cache_dir */
@ -2453,17 +2454,18 @@ struct read_data {
const unsigned char *end; const unsigned char *end;
}; };


static void stat_data_from_disk(struct stat_data *to, const struct stat_data *from) static void stat_data_from_disk(struct stat_data *to, const unsigned char *data)
{ {
to->sd_ctime.sec = get_be32(&from->sd_ctime.sec); memcpy(to, data, sizeof(*to));
to->sd_ctime.nsec = get_be32(&from->sd_ctime.nsec); to->sd_ctime.sec = ntohl(to->sd_ctime.sec);
to->sd_mtime.sec = get_be32(&from->sd_mtime.sec); to->sd_ctime.nsec = ntohl(to->sd_ctime.nsec);
to->sd_mtime.nsec = get_be32(&from->sd_mtime.nsec); to->sd_mtime.sec = ntohl(to->sd_mtime.sec);
to->sd_dev = get_be32(&from->sd_dev); to->sd_mtime.nsec = ntohl(to->sd_mtime.nsec);
to->sd_ino = get_be32(&from->sd_ino); to->sd_dev = ntohl(to->sd_dev);
to->sd_uid = get_be32(&from->sd_uid); to->sd_ino = ntohl(to->sd_ino);
to->sd_gid = get_be32(&from->sd_gid); to->sd_uid = ntohl(to->sd_uid);
to->sd_size = get_be32(&from->sd_size); to->sd_gid = ntohl(to->sd_gid);
to->sd_size = ntohl(to->sd_size);
} }


static int read_one_dir(struct untracked_cache_dir **untracked_, static int read_one_dir(struct untracked_cache_dir **untracked_,
@ -2538,7 +2540,7 @@ static void read_stat(size_t pos, void *cb)
rd->data = rd->end + 1; rd->data = rd->end + 1;
return; return;
} }
stat_data_from_disk(&ud->stat_data, (struct stat_data *)rd->data); stat_data_from_disk(&ud->stat_data, rd->data);
rd->data += sizeof(struct stat_data); rd->data += sizeof(struct stat_data);
ud->valid = 1; ud->valid = 1;
} }
@ -2556,22 +2558,22 @@ static void read_sha1(size_t pos, void *cb)
} }


static void load_sha1_stat(struct sha1_stat *sha1_stat, static void load_sha1_stat(struct sha1_stat *sha1_stat,
const struct stat_data *stat, const unsigned char *data,
const unsigned char *sha1) const unsigned char *sha1)
{ {
stat_data_from_disk(&sha1_stat->stat, stat); stat_data_from_disk(&sha1_stat->stat, data);
hashcpy(sha1_stat->sha1, sha1); hashcpy(sha1_stat->sha1, sha1);
sha1_stat->valid = 1; sha1_stat->valid = 1;
} }


struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz) struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz)
{ {
const struct ondisk_untracked_cache *ouc;
struct untracked_cache *uc; struct untracked_cache *uc;
struct read_data rd; struct read_data rd;
const unsigned char *next = data, *end = (const unsigned char *)data + sz; const unsigned char *next = data, *end = (const unsigned char *)data + sz;
const char *ident; const char *ident;
int ident_len, len; int ident_len, len;
const char *exclude_per_dir;


if (sz <= 1 || end[-1] != '\0') if (sz <= 1 || end[-1] != '\0')
return NULL; return NULL;
@ -2583,21 +2585,23 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
ident = (const char *)next; ident = (const char *)next;
next += ident_len; next += ident_len;


ouc = (const struct ondisk_untracked_cache *)next;
if (next + ouc_size(0) > end) if (next + ouc_size(0) > end)
return NULL; return NULL;


uc = xcalloc(1, sizeof(*uc)); uc = xcalloc(1, sizeof(*uc));
strbuf_init(&uc->ident, ident_len); strbuf_init(&uc->ident, ident_len);
strbuf_add(&uc->ident, ident, ident_len); strbuf_add(&uc->ident, ident, ident_len);
load_sha1_stat(&uc->ss_info_exclude, &ouc->info_exclude_stat, load_sha1_stat(&uc->ss_info_exclude,
ouc->info_exclude_sha1); next + ouc_offset(info_exclude_stat),
load_sha1_stat(&uc->ss_excludes_file, &ouc->excludes_file_stat, next + ouc_offset(info_exclude_sha1));
ouc->excludes_file_sha1); load_sha1_stat(&uc->ss_excludes_file,
uc->dir_flags = get_be32(&ouc->dir_flags); next + ouc_offset(excludes_file_stat),
uc->exclude_per_dir = xstrdup(ouc->exclude_per_dir); next + ouc_offset(excludes_file_sha1));
uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
exclude_per_dir = (const char *)next + ouc_offset(exclude_per_dir);
uc->exclude_per_dir = xstrdup(exclude_per_dir);
/* NUL after exclude_per_dir is covered by sizeof(*ouc) */ /* NUL after exclude_per_dir is covered by sizeof(*ouc) */
next += ouc_size(strlen(ouc->exclude_per_dir)); next += ouc_size(strlen(exclude_per_dir));
if (next >= end) if (next >= end)
goto done2; goto done2;