Merge branch 'jk/pack-header-parse-alignment-fix'

It was possible for "git unpack-objects" and "git index-pack" to
make an unaligned access, which has been corrected.

* jk/pack-header-parse-alignment-fix:
  index-pack, unpack-objects: use skip_prefix to avoid magic number
  index-pack, unpack-objects: use get_be32() for reading pack header
  parse_pack_header_option(): avoid unaligned memory writes
  packfile: factor out --pack_header argument parsing
  bswap.h: squelch potential sparse -Wcast-truncate warnings
This commit is contained in:
Junio C Hamano
2025-01-28 13:02:23 -08:00
6 changed files with 64 additions and 50 deletions

View File

@@ -380,16 +380,18 @@ static const char *open_pack_file(const char *pack_name)
static void parse_pack_header(void) static void parse_pack_header(void)
{ {
struct pack_header *hdr = fill(sizeof(struct pack_header)); unsigned char *hdr = fill(sizeof(struct pack_header));
/* Header consistency check */ /* Header consistency check */
if (hdr->hdr_signature != htonl(PACK_SIGNATURE)) if (get_be32(hdr) != PACK_SIGNATURE)
die(_("pack signature mismatch")); die(_("pack signature mismatch"));
if (!pack_version_ok(hdr->hdr_version)) hdr += 4;
if (!pack_version_ok_native(get_be32(hdr)))
die(_("pack version %"PRIu32" unsupported"), die(_("pack version %"PRIu32" unsupported"),
ntohl(hdr->hdr_version)); get_be32(hdr));
hdr += 4;
nr_objects = ntohl(hdr->hdr_entries); nr_objects = get_be32(hdr);
use(sizeof(struct pack_header)); use(sizeof(struct pack_header));
} }
@@ -1956,19 +1958,11 @@ int cmd_index_pack(int argc,
warning(_("no threads support, ignoring %s"), arg); warning(_("no threads support, ignoring %s"), arg);
nr_threads = 1; nr_threads = 1;
} }
} else if (starts_with(arg, "--pack_header=")) { } else if (skip_prefix(arg, "--pack_header=", &arg)) {
struct pack_header *hdr; if (parse_pack_header_option(arg,
char *c; input_buffer,
&input_len) < 0)
hdr = (struct pack_header *)input_buffer; die(_("bad --pack_header: %s"), arg);
hdr->hdr_signature = htonl(PACK_SIGNATURE);
hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10));
if (*c != ',')
die(_("bad %s"), arg);
hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10));
if (*c)
die(_("bad %s"), arg);
input_len = sizeof(*hdr);
} else if (!strcmp(arg, "-v")) { } else if (!strcmp(arg, "-v")) {
verbose = 1; verbose = 1;
} else if (!strcmp(arg, "--progress-title")) { } else if (!strcmp(arg, "--progress-title")) {

View File

@@ -18,6 +18,7 @@
#include "progress.h" #include "progress.h"
#include "decorate.h" #include "decorate.h"
#include "fsck.h" #include "fsck.h"
#include "packfile.h"
static int dry_run, quiet, recover, has_errors, strict; static int dry_run, quiet, recover, has_errors, strict;
static const char unpack_usage[] = "git unpack-objects [-n] [-q] [-r] [--strict]"; static const char unpack_usage[] = "git unpack-objects [-n] [-q] [-r] [--strict]";
@@ -578,15 +579,16 @@ static void unpack_one(unsigned nr)
static void unpack_all(void) static void unpack_all(void)
{ {
int i; int i;
struct pack_header *hdr = fill(sizeof(struct pack_header)); unsigned char *hdr = fill(sizeof(struct pack_header));
nr_objects = ntohl(hdr->hdr_entries); if (get_be32(hdr) != PACK_SIGNATURE)
if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE)
die("bad pack file"); die("bad pack file");
if (!pack_version_ok(hdr->hdr_version)) hdr += 4;
if (!pack_version_ok_native(get_be32(hdr)))
die("unknown pack file version %"PRIu32, die("unknown pack file version %"PRIu32,
ntohl(hdr->hdr_version)); get_be32(hdr));
hdr += 4;
nr_objects = get_be32(hdr);
use(sizeof(struct pack_header)); use(sizeof(struct pack_header));
if (!quiet) if (!quiet)
@@ -647,19 +649,10 @@ int cmd_unpack_objects(int argc,
fsck_set_msg_types(&fsck_options, arg); fsck_set_msg_types(&fsck_options, arg);
continue; continue;
} }
if (starts_with(arg, "--pack_header=")) { if (skip_prefix(arg, "--pack_header=", &arg)) {
struct pack_header *hdr; if (parse_pack_header_option(arg,
char *c; buffer, &len) < 0)
die(_("bad --pack_header: %s"), arg);
hdr = (struct pack_header *)buffer;
hdr->hdr_signature = htonl(PACK_SIGNATURE);
hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10));
if (*c != ',')
die("bad %s", arg);
hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10));
if (*c)
die("bad %s", arg);
len = sizeof(*hdr);
continue; continue;
} }
if (skip_prefix(arg, "--max-input-size=", &arg)) { if (skip_prefix(arg, "--max-input-size=", &arg)) {

View File

@@ -171,23 +171,23 @@ static inline uint64_t get_be64(const void *ptr)
static inline void put_be32(void *ptr, uint32_t value) static inline void put_be32(void *ptr, uint32_t value)
{ {
unsigned char *p = ptr; unsigned char *p = ptr;
p[0] = value >> 24; p[0] = (value >> 24) & 0xff;
p[1] = value >> 16; p[1] = (value >> 16) & 0xff;
p[2] = value >> 8; p[2] = (value >> 8) & 0xff;
p[3] = value >> 0; p[3] = (value >> 0) & 0xff;
} }
static inline void put_be64(void *ptr, uint64_t value) static inline void put_be64(void *ptr, uint64_t value)
{ {
unsigned char *p = ptr; unsigned char *p = ptr;
p[0] = value >> 56; p[0] = (value >> 56) & 0xff;
p[1] = value >> 48; p[1] = (value >> 48) & 0xff;
p[2] = value >> 40; p[2] = (value >> 40) & 0xff;
p[3] = value >> 32; p[3] = (value >> 32) & 0xff;
p[4] = value >> 24; p[4] = (value >> 24) & 0xff;
p[5] = value >> 16; p[5] = (value >> 16) & 0xff;
p[6] = value >> 8; p[6] = (value >> 8) & 0xff;
p[7] = value >> 0; p[7] = (value >> 0) & 0xff;
} }
#endif /* COMPAT_BSWAP_H */ #endif /* COMPAT_BSWAP_H */

3
pack.h
View File

@@ -13,7 +13,8 @@ struct repository;
*/ */
#define PACK_SIGNATURE 0x5041434b /* "PACK" */ #define PACK_SIGNATURE 0x5041434b /* "PACK" */
#define PACK_VERSION 2 #define PACK_VERSION 2
#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3)) #define pack_version_ok(v) pack_version_ok_native(ntohl(v))
#define pack_version_ok_native(v) ((v) == 2 || (v) == 3)
struct pack_header { struct pack_header {
uint32_t hdr_signature; uint32_t hdr_signature;
uint32_t hdr_version; uint32_t hdr_version;

View File

@@ -2315,3 +2315,23 @@ int is_promisor_object(struct repository *r, const struct object_id *oid)
} }
return oidset_contains(&promisor_objects, oid); return oidset_contains(&promisor_objects, oid);
} }
int parse_pack_header_option(const char *in, unsigned char *out, unsigned int *len)
{
unsigned char *hdr;
char *c;
hdr = out;
put_be32(hdr, PACK_SIGNATURE);
hdr += 4;
put_be32(hdr, strtoul(in, &c, 10));
hdr += 4;
if (*c != ',')
return -1;
put_be32(hdr, strtoul(c + 1, &c, 10));
hdr += 4;
if (*c)
return -1;
*len = hdr - out;
return 0;
}

View File

@@ -216,4 +216,10 @@ int is_promisor_object(struct repository *r, const struct object_id *oid);
int load_idx(const char *path, const unsigned int hashsz, void *idx_map, int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
size_t idx_size, struct packed_git *p); size_t idx_size, struct packed_git *p);
/*
* Parse a --pack_header option as accepted by index-pack and unpack-objects,
* turning it into the matching bytes we'd find in a pack.
*/
int parse_pack_header_option(const char *in, unsigned char *out, unsigned int *len);
#endif #endif