Skip to content

Commit 8cd4249

Browse files
peffgitster
authored andcommitted
interpret_branch_name: always respect "namelen" parameter
interpret_branch_name gets passed a "name" buffer to parse, along with a "namelen" parameter representing its length. If "namelen" is zero, we fallback to the NUL-terminated string-length of "name". However, it does not necessarily follow that if we have gotten a non-zero "namelen", it is the NUL-terminated string-length of "name". E.g., when get_sha1() is parsing "foo:bar", we will be asked to operate only on the first three characters. Yet in interpret_branch_name and its helpers, we use string functions like strchr() to operate on "name", looking past the length we were given. This can result in us mis-parsing object names. We should instead be limiting our search to "namelen" bytes. There are three distinct types of object names this patch addresses: - The intrepret_empty_at helper uses strchr to find the next @-expression after our potential empty-at. In an expression like "@:foo@bar", it erroneously thinks that the second "@" is relevant, even if we were asked only to look at the first character. This case is easy to trigger (and we test it in this patch). - When finding the initial @-mark for @{upstream}, we use strchr. This means we might treat "foo:@{upstream}" as the upstream for "foo:", even though we were asked only to look at "foo". We cannot test this one in practice, because it is masked by another bug (which is fixed in the next patch). - The interpret_nth_prior_checkout helper did not receive the name length at all. This turns out not to be a problem in practice, though, because its parsing is so limited: it always starts from the far-left of the string, and will not tolerate a colon (which is currently the only way to get a smaller-than-strlen "namelen"). However, it's still worth fixing to make the code more obviously correct, and to future-proof us against callers with more exotic buffers. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f278f40 commit 8cd4249

File tree

2 files changed

+24
-8
lines changed

2 files changed

+24
-8
lines changed

sha1_name.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ static inline int upstream_mark(const char *string, int len)
430430
}
431431

432432
static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
433-
static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf);
433+
static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
434434

435435
static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
436436
{
@@ -492,7 +492,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
492492
struct strbuf buf = STRBUF_INIT;
493493
int detached;
494494

495-
if (interpret_nth_prior_checkout(str, &buf) > 0) {
495+
if (interpret_nth_prior_checkout(str, len, &buf) > 0) {
496496
detached = (buf.len == 40 && !get_sha1_hex(buf.buf, sha1));
497497
strbuf_release(&buf);
498498
if (detached)
@@ -931,17 +931,20 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
931931
* Parse @{-N} syntax, return the number of characters parsed
932932
* if successful; otherwise signal an error with negative value.
933933
*/
934-
static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf)
934+
static int interpret_nth_prior_checkout(const char *name, int namelen,
935+
struct strbuf *buf)
935936
{
936937
long nth;
937938
int retval;
938939
struct grab_nth_branch_switch_cbdata cb;
939940
const char *brace;
940941
char *num_end;
941942

943+
if (namelen < 4)
944+
return -1;
942945
if (name[0] != '@' || name[1] != '{' || name[2] != '-')
943946
return -1;
944-
brace = strchr(name, '}');
947+
brace = memchr(name, '}', namelen);
945948
if (!brace)
946949
return -1;
947950
nth = strtol(name + 3, &num_end, 10);
@@ -1014,7 +1017,7 @@ static int interpret_empty_at(const char *name, int namelen, int len, struct str
10141017
return -1;
10151018

10161019
/* make sure it's a single @, or @@{.*}, not @foo */
1017-
next = strchr(name + len + 1, '@');
1020+
next = memchr(name + len + 1, '@', namelen - len - 1);
10181021
if (next && next[1] != '{')
10191022
return -1;
10201023
if (!next)
@@ -1120,7 +1123,7 @@ static int interpret_upstream_mark(const char *name, int namelen,
11201123
int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
11211124
{
11221125
char *at;
1123-
int len = interpret_nth_prior_checkout(name, buf);
1126+
int len = interpret_nth_prior_checkout(name, namelen, buf);
11241127

11251128
if (!namelen)
11261129
namelen = strlen(name);
@@ -1134,7 +1137,7 @@ int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
11341137
return reinterpret(name, namelen, len, buf);
11351138
}
11361139

1137-
at = strchr(name, '@');
1140+
at = memchr(name, '@', namelen);
11381141
if (!at)
11391142
return -1;
11401143

t/t1508-at-combinations.sh

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ check() {
99
if test '$2' = 'commit'
1010
then
1111
git log -1 --format=%s '$1' >actual
12-
else
12+
elif test '$2' = 'ref'
13+
then
1314
git rev-parse --symbolic-full-name '$1' >actual
15+
else
16+
git cat-file -p '$1' >actual
1417
fi &&
1518
test_cmp expect actual
1619
"
@@ -82,4 +85,14 @@ check HEAD ref refs/heads/old-branch
8285
check "HEAD@{1}" commit new-two
8386
check "@{1}" commit old-one
8487

88+
test_expect_success 'create path with @' '
89+
echo content >normal &&
90+
echo content >fun@ny &&
91+
git add normal fun@ny &&
92+
git commit -m "funny path"
93+
'
94+
95+
check "@:normal" blob content
96+
check "@:fun@ny" blob content
97+
8598
test_done

0 commit comments

Comments
 (0)