Skip to content

Commit af49c6d

Browse files
peffgitster
authored andcommitted
add reentrant variants of sha1_to_hex and find_unique_abbrev
The sha1_to_hex and find_unique_abbrev functions always write into reusable static buffers. There are a few problems with this: - future calls overwrite our result. This is especially annoying with find_unique_abbrev, which does not have a ring of buffers, so you cannot even printf() a result that has two abbreviated sha1s. - if you want to put the result into another buffer, we often strcpy, which looks suspicious when auditing for overflows. This patch introduces sha1_to_hex_r and find_unique_abbrev_r, which write into a user-provided buffer. Of course this is just punting on the overflow-auditing, as the buffer obviously needs to be GIT_SHA1_HEXSZ + 1 bytes. But it is much easier to audit, since that is a well-known size. We retain the non-reentrant forms, which just become thin wrappers around the reentrant ones. This patch also adds a strbuf variant of find_unique_abbrev, which will be handy in later patches. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 399ad55 commit af49c6d

File tree

5 files changed

+67
-10
lines changed

5 files changed

+67
-10
lines changed

cache.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,24 @@ extern char *sha1_pack_name(const unsigned char *sha1);
785785
*/
786786
extern char *sha1_pack_index_name(const unsigned char *sha1);
787787

788-
extern const char *find_unique_abbrev(const unsigned char *sha1, int);
788+
/*
789+
* Return an abbreviated sha1 unique within this repository's object database.
790+
* The result will be at least `len` characters long, and will be NUL
791+
* terminated.
792+
*
793+
* The non-`_r` version returns a static buffer which will be overwritten by
794+
* subsequent calls.
795+
*
796+
* The `_r` variant writes to a buffer supplied by the caller, which must be at
797+
* least `GIT_SHA1_HEXSZ + 1` bytes. The return value is the number of bytes
798+
* written (excluding the NUL terminator).
799+
*
800+
* Note that while this version avoids the static buffer, it is not fully
801+
* reentrant, as it calls into other non-reentrant git code.
802+
*/
803+
extern const char *find_unique_abbrev(const unsigned char *sha1, int len);
804+
extern int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len);
805+
789806
extern const unsigned char null_sha1[GIT_SHA1_RAWSZ];
790807

791808
static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
@@ -1067,6 +1084,18 @@ extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
10671084
extern int get_sha1_hex(const char *hex, unsigned char *sha1);
10681085
extern int get_oid_hex(const char *hex, struct object_id *sha1);
10691086

1087+
/*
1088+
* Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
1089+
* and writes the NUL-terminated output to the buffer `out`, which must be at
1090+
* least `GIT_SHA1_HEXSZ + 1` bytes, and returns a pointer to out for
1091+
* convenience.
1092+
*
1093+
* The non-`_r` variant returns a static buffer, but uses a ring of 4
1094+
* buffers, making it safe to make multiple calls for a single statement, like:
1095+
*
1096+
* printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
1097+
*/
1098+
extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);
10701099
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
10711100
extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
10721101

hex.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,10 @@ int get_oid_hex(const char *hex, struct object_id *oid)
6161
return get_sha1_hex(hex, oid->hash);
6262
}
6363

64-
char *sha1_to_hex(const unsigned char *sha1)
64+
char *sha1_to_hex_r(char *buffer, const unsigned char *sha1)
6565
{
66-
static int bufno;
67-
static char hexbuffer[4][GIT_SHA1_HEXSZ + 1];
6866
static const char hex[] = "0123456789abcdef";
69-
char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
67+
char *buf = buffer;
7068
int i;
7169

7270
for (i = 0; i < GIT_SHA1_RAWSZ; i++) {
@@ -79,6 +77,13 @@ char *sha1_to_hex(const unsigned char *sha1)
7977
return buffer;
8078
}
8179

80+
char *sha1_to_hex(const unsigned char *sha1)
81+
{
82+
static int bufno;
83+
static char hexbuffer[4][GIT_SHA1_HEXSZ + 1];
84+
return sha1_to_hex_r(hexbuffer[3 & ++bufno], sha1);
85+
}
86+
8287
char *oid_to_hex(const struct object_id *oid)
8388
{
8489
return sha1_to_hex(oid->hash);

sha1_name.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -368,14 +368,13 @@ int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
368368
return ds.ambiguous;
369369
}
370370

371-
const char *find_unique_abbrev(const unsigned char *sha1, int len)
371+
int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
372372
{
373373
int status, exists;
374-
static char hex[41];
375374

376-
memcpy(hex, sha1_to_hex(sha1), 40);
375+
sha1_to_hex_r(hex, sha1);
377376
if (len == 40 || !len)
378-
return hex;
377+
return 40;
379378
exists = has_sha1_file(sha1);
380379
while (len < 40) {
381380
unsigned char sha1_ret[20];
@@ -384,10 +383,17 @@ const char *find_unique_abbrev(const unsigned char *sha1, int len)
384383
? !status
385384
: status == SHORT_NAME_NOT_FOUND) {
386385
hex[len] = 0;
387-
return hex;
386+
return len;
388387
}
389388
len++;
390389
}
390+
return len;
391+
}
392+
393+
const char *find_unique_abbrev(const unsigned char *sha1, int len)
394+
{
395+
static char hex[GIT_SHA1_HEXSZ + 1];
396+
find_unique_abbrev_r(hex, sha1, len);
391397
return hex;
392398
}
393399

strbuf.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,3 +743,12 @@ void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm)
743743
}
744744
strbuf_setlen(sb, sb->len + len);
745745
}
746+
747+
void strbuf_add_unique_abbrev(struct strbuf *sb, const unsigned char *sha1,
748+
int abbrev_len)
749+
{
750+
int r;
751+
strbuf_grow(sb, GIT_SHA1_HEXSZ + 1);
752+
r = find_unique_abbrev_r(sb->buf + sb->len, sha1, abbrev_len);
753+
strbuf_setlen(sb, sb->len + r);
754+
}

strbuf.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,14 @@ static inline struct strbuf **strbuf_split(const struct strbuf *sb,
474474
*/
475475
extern void strbuf_list_free(struct strbuf **);
476476

477+
/**
478+
* Add the abbreviation, as generated by find_unique_abbrev, of `sha1` to
479+
* the strbuf `sb`.
480+
*/
481+
extern void strbuf_add_unique_abbrev(struct strbuf *sb,
482+
const unsigned char *sha1,
483+
int abbrev_len);
484+
477485
/**
478486
* Launch the user preferred editor to edit a file and fill the buffer
479487
* with the file's contents upon the user completing their editing. The

0 commit comments

Comments
 (0)