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>

René Scharfe committed Jul 16, 2017 at 14:17 UTC 268ba20110f9e6aca3a7bf8b256f7a8fbfd2dab9
1 file changed +27 -23
dir.c
+27 -23
@@ -2290,7 +2290,8 @@ struct ondisk_untracked_cache {
2290 char exclude_per_dir[FLEX_ARRAY];
2291 };
2292
2293 -#define ouc_size(len) (offsetof(struct ondisk_untracked_cache, exclude_per_dir) + len + 1)
2293 +#define ouc_offset(x) offsetof(struct ondisk_untracked_cache, x)
2294 +#define ouc_size(len) (ouc_offset(exclude_per_dir) + len + 1)
2295
2296 struct write_data {
2297 int index; /* number of written untracked_cache_dir */
@@ -2453,17 +2454,18 @@ struct read_data {
2454 const unsigned char *end;
2455 };
2456
2456 -static void stat_data_from_disk(struct stat_data *to, const struct stat_data *from)
2457 +static void stat_data_from_disk(struct stat_data *to, const unsigned char *data)
2458 {
2458 - to->sd_ctime.sec = get_be32(&from->sd_ctime.sec);
2459 - to->sd_ctime.nsec = get_be32(&from->sd_ctime.nsec);
2460 - to->sd_mtime.sec = get_be32(&from->sd_mtime.sec);
2461 - to->sd_mtime.nsec = get_be32(&from->sd_mtime.nsec);
2462 - to->sd_dev = get_be32(&from->sd_dev);
2463 - to->sd_ino = get_be32(&from->sd_ino);
2464 - to->sd_uid = get_be32(&from->sd_uid);
2465 - to->sd_gid = get_be32(&from->sd_gid);
2466 - to->sd_size = get_be32(&from->sd_size);
2459 + memcpy(to, data, sizeof(*to));
2460 + to->sd_ctime.sec = ntohl(to->sd_ctime.sec);
2461 + to->sd_ctime.nsec = ntohl(to->sd_ctime.nsec);
2462 + to->sd_mtime.sec = ntohl(to->sd_mtime.sec);
2463 + to->sd_mtime.nsec = ntohl(to->sd_mtime.nsec);
2464 + to->sd_dev = ntohl(to->sd_dev);
2465 + to->sd_ino = ntohl(to->sd_ino);
2466 + to->sd_uid = ntohl(to->sd_uid);
2467 + to->sd_gid = ntohl(to->sd_gid);
2468 + to->sd_size = ntohl(to->sd_size);
2469 }
2470
2471 static int read_one_dir(struct untracked_cache_dir **untracked_,
@@ -2538,7 +2540,7 @@ static void read_stat(size_t pos, void *cb)
2540 rd->data = rd->end + 1;
2541 return;
2542 }
2541 - stat_data_from_disk(&ud->stat_data, (struct stat_data *)rd->data);
2543 + stat_data_from_disk(&ud->stat_data, rd->data);
2544 rd->data += sizeof(struct stat_data);
2545 ud->valid = 1;
2546 }
@@ -2556,22 +2558,22 @@ static void read_sha1(size_t pos, void *cb)
2558 }
2559
2560 static void load_sha1_stat(struct sha1_stat *sha1_stat,
2559 - const struct stat_data *stat,
2561 + const unsigned char *data,
2562 const unsigned char *sha1)
2563 {
2562 - stat_data_from_disk(&sha1_stat->stat, stat);
2564 + stat_data_from_disk(&sha1_stat->stat, data);
2565 hashcpy(sha1_stat->sha1, sha1);
2566 sha1_stat->valid = 1;
2567 }
2568
2569 struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz)
2570 {
2569 - const struct ondisk_untracked_cache *ouc;
2571 struct untracked_cache *uc;
2572 struct read_data rd;
2573 const unsigned char *next = data, *end = (const unsigned char *)data + sz;
2574 const char *ident;
2575 int ident_len, len;
2576 + const char *exclude_per_dir;
2577
2578 if (sz <= 1 || end[-1] != '\0')
2579 return NULL;
@@ -2583,21 +2585,23 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
2585 ident = (const char *)next;
2586 next += ident_len;
2587
2586 - ouc = (const struct ondisk_untracked_cache *)next;
2588 if (next + ouc_size(0) > end)
2589 return NULL;
2590
2591 uc = xcalloc(1, sizeof(*uc));
2592 strbuf_init(&uc->ident, ident_len);
2593 strbuf_add(&uc->ident, ident, ident_len);
2593 - load_sha1_stat(&uc->ss_info_exclude, &ouc->info_exclude_stat,
2594 - ouc->info_exclude_sha1);
2595 - load_sha1_stat(&uc->ss_excludes_file, &ouc->excludes_file_stat,
2596 - ouc->excludes_file_sha1);
2597 - uc->dir_flags = get_be32(&ouc->dir_flags);
2598 - uc->exclude_per_dir = xstrdup(ouc->exclude_per_dir);
2594 + load_sha1_stat(&uc->ss_info_exclude,
2595 + next + ouc_offset(info_exclude_stat),
2596 + next + ouc_offset(info_exclude_sha1));
2597 + load_sha1_stat(&uc->ss_excludes_file,
2598 + next + ouc_offset(excludes_file_stat),
2599 + next + ouc_offset(excludes_file_sha1));
2600 + uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
2601 + exclude_per_dir = (const char *)next + ouc_offset(exclude_per_dir);
2602 + uc->exclude_per_dir = xstrdup(exclude_per_dir);
2603 /* NUL after exclude_per_dir is covered by sizeof(*ouc) */
2600 - next += ouc_size(strlen(ouc->exclude_per_dir));
2604 + next += ouc_size(strlen(exclude_per_dir));
2605 if (next >= end)
2606 goto done2;
2607