* [PATCH] wt-status: use strncmp() for length-limited string comparison @ 2015-11-06 22:47 René Scharfe 2015-11-24 21:36 ` Jeff King 0 siblings, 1 reply; 8+ messages in thread From: René Scharfe @ 2015-11-06 22:47 UTC (permalink / raw) To: Git List; +Cc: Junio C Hamano, Matthieu Moy When a branch name is longer than four characters, memcmp() can read past the end of the string literal "HEAD". Use strncmp() instead, which stops at the end of a string. This fixes the following test failures with AddressSanitizer: t3203-branch-output.sh (Wstat: 256 Tests: 18 Failed: 4) Failed tests: 12, 15-17 Non-zero exit status: 1 t3412-rebase-root.sh (Wstat: 256 Tests: 31 Failed: 3) Failed tests: 28-29, 31 Non-zero exit status: 1 t3507-cherry-pick-conflict.sh (Wstat: 256 Tests: 31 Failed: 4) Failed tests: 14, 29-31 Non-zero exit status: 1 t3510-cherry-pick-sequence.sh (Wstat: 256 Tests: 39 Failed: 14) Failed tests: 17, 22-26, 28-30, 34-35, 37-39 Non-zero exit status: 1 t3420-rebase-autostash.sh (Wstat: 256 Tests: 28 Failed: 4) Failed tests: 24-27 Non-zero exit status: 1 t3404-rebase-interactive.sh (Wstat: 256 Tests: 91 Failed: 57) Failed tests: 17, 19, 21-42, 44, 46-74, 77, 81-82 Non-zero exit status: 1 t3900-i18n-commit.sh (Wstat: 256 Tests: 34 Failed: 1) Failed test: 34 Non-zero exit status: 1 t5407-post-rewrite-hook.sh (Wstat: 256 Tests: 14 Failed: 6) Failed tests: 9-14 Non-zero exit status: 1 t7001-mv.sh (Wstat: 256 Tests: 46 Failed: 5) Failed tests: 39-43 Non-zero exit status: 1 t7509-commit.sh (Wstat: 256 Tests: 12 Failed: 2) Failed tests: 11-12 Non-zero exit status: 1 t7512-status-help.sh (Wstat: 256 Tests: 39 Failed: 35) Failed tests: 5-39 Non-zero exit status: 1 t6030-bisect-porcelain.sh (Wstat: 256 Tests: 70 Failed: 1) Failed test: 13 Non-zero exit status: 1 Signed-off-by: Rene Scharfe <l.s.r@web.de> --- wt-status.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wt-status.c b/wt-status.c index 435fc28..8dc281b 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1319,7 +1319,7 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1, hashcpy(cb->nsha1, nsha1); for (end = target; *end && *end != '\n'; end++) ; - if (!memcmp(target, "HEAD", end - target)) { + if (!strncmp(target, "HEAD", end - target)) { /* HEAD is relative. Resolve it to the right reflog entry. */ strbuf_addstr(&cb->buf, find_unique_abbrev(nsha1, DEFAULT_ABBREV)); -- 2.6.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] wt-status: use strncmp() for length-limited string comparison 2015-11-06 22:47 [PATCH] wt-status: use strncmp() for length-limited string comparison René Scharfe @ 2015-11-24 21:36 ` Jeff King 2015-11-25 2:16 ` René Scharfe 0 siblings, 1 reply; 8+ messages in thread From: Jeff King @ 2015-11-24 21:36 UTC (permalink / raw) To: René Scharfe; +Cc: Git List, Junio C Hamano, Matthieu Moy On Fri, Nov 06, 2015 at 11:47:03PM +0100, René Scharfe wrote: > When a branch name is longer than four characters, memcmp() can read > past the end of the string literal "HEAD". Use strncmp() instead, which > stops at the end of a string. This fixes the following test failures > with AddressSanitizer: Hmm. I think this is mostly harmless, as a comparison like: memcmp("HEAD and more", "HEAD", strlen("HEAD")) would yield non-zero when we compare the NUL in the second string to whatever is in the first. So I assume what is going on is that memcmp is doing larger compares than byte by byte, and is examining 4 or 8 bytes starting at that NUL. The outcome is equivalent, but we do touch memory that is not ours, so I think this is a positive direction in that sense. But... > diff --git a/wt-status.c b/wt-status.c > index 435fc28..8dc281b 100644 > --- a/wt-status.c > +++ b/wt-status.c > @@ -1319,7 +1319,7 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1, > hashcpy(cb->nsha1, nsha1); > for (end = target; *end && *end != '\n'; end++) > ; > - if (!memcmp(target, "HEAD", end - target)) { > + if (!strncmp(target, "HEAD", end - target)) { This will match prefixes like "HEA" in the target, won't it? I think you want something more like: if (end - target == 4 && !memcmp(target, "HEAD", 4)) I tried to think of a way that didn't involve a magic number. The best I came up with is: if (skip_prefix(target, "HEAD", &v) && v == end) but that requires an extra variable, and is arguably more obfuscated. -Peff ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] wt-status: use strncmp() for length-limited string comparison 2015-11-24 21:36 ` Jeff King @ 2015-11-25 2:16 ` René Scharfe 2015-11-25 9:15 ` Jeff King 0 siblings, 1 reply; 8+ messages in thread From: René Scharfe @ 2015-11-25 2:16 UTC (permalink / raw) To: Jeff King; +Cc: Git List, Junio C Hamano, Matthieu Moy Am 24.11.2015 um 22:36 schrieb Jeff King: > On Fri, Nov 06, 2015 at 11:47:03PM +0100, René Scharfe wrote: > >> When a branch name is longer than four characters, memcmp() can read >> past the end of the string literal "HEAD". Use strncmp() instead, which >> stops at the end of a string. This fixes the following test failures >> with AddressSanitizer: > > Hmm. I think this is mostly harmless, as a comparison like: > > memcmp("HEAD and more", "HEAD", strlen("HEAD")) > > would yield non-zero when we compare the NUL in the second string to > whatever is in the first. So I assume what is going on is that memcmp is > doing larger compares than byte by byte, and is examining 4 or 8 bytes > starting at that NUL. > > The outcome is equivalent, but we do touch memory that is not ours, so I > think this is a positive direction in that sense. Yes, except it should be strlen("HEAD and more") in your example code; with strlen("HEAD") it would compare just 4 bytes and return 0. > But... > >> diff --git a/wt-status.c b/wt-status.c >> index 435fc28..8dc281b 100644 >> --- a/wt-status.c >> +++ b/wt-status.c >> @@ -1319,7 +1319,7 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1, >> hashcpy(cb->nsha1, nsha1); >> for (end = target; *end && *end != '\n'; end++) >> ; >> - if (!memcmp(target, "HEAD", end - target)) { >> + if (!strncmp(target, "HEAD", end - target)) { > > This will match prefixes like "HEA" in the target, won't it? Oww, yes. :-/ NB: The existing code does the same. > I think you want something more like: > > if (end - target == 4 && !memcmp(target, "HEAD", 4)) > > I tried to think of a way that didn't involve a magic number. The best I > came up with is: > > if (skip_prefix(target, "HEAD", &v) && v == end) > > but that requires an extra variable, and is arguably more obfuscated. Using one more variable isn't that bad, as long as it gets a fitting name. Or we could reuse "end" (I'm not worrying about scanning "HEAD" twice very much): diff --git a/wt-status.c b/wt-status.c index 435fc28..96a731e 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1317,14 +1317,14 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1, target += strlen(" to "); strbuf_reset(&cb->buf); hashcpy(cb->nsha1, nsha1); - for (end = target; *end && *end != '\n'; end++) - ; - if (!memcmp(target, "HEAD", end - target)) { + if (skip_prefix(target, "HEAD", &end) && (!*end || *end == '\n')) { /* HEAD is relative. Resolve it to the right reflog entry. */ strbuf_addstr(&cb->buf, find_unique_abbrev(nsha1, DEFAULT_ABBREV)); return 1; } + for (end = target; *end && *end != '\n'; end++) + ; strbuf_add(&cb->buf, target, end - target); return 1; } ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] wt-status: use strncmp() for length-limited string comparison 2015-11-25 2:16 ` René Scharfe @ 2015-11-25 9:15 ` Jeff King 2015-11-25 10:29 ` Matthieu Moy 2015-11-25 14:10 ` [PATCH v2] wt-status: correct and simplify check for detached HEAD René Scharfe 0 siblings, 2 replies; 8+ messages in thread From: Jeff King @ 2015-11-25 9:15 UTC (permalink / raw) To: René Scharfe; +Cc: Git List, Junio C Hamano, Matthieu Moy On Wed, Nov 25, 2015 at 03:16:49AM +0100, René Scharfe wrote: > > Hmm. I think this is mostly harmless, as a comparison like: > > > > memcmp("HEAD and more", "HEAD", strlen("HEAD")) > [...] > > Yes, except it should be strlen("HEAD and more") in your example code; > with strlen("HEAD") it would compare just 4 bytes and return 0. Whoops, yeah. Thank you for figuring out what I meant. :) > Using one more variable isn't that bad, as long as it gets a fitting > name. Or we could reuse "end" (I'm not worrying about scanning "HEAD" > twice very much): > > diff --git a/wt-status.c b/wt-status.c > index 435fc28..96a731e 100644 > --- a/wt-status.c > +++ b/wt-status.c > @@ -1317,14 +1317,14 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1, > target += strlen(" to "); > strbuf_reset(&cb->buf); > hashcpy(cb->nsha1, nsha1); > - for (end = target; *end && *end != '\n'; end++) > - ; > - if (!memcmp(target, "HEAD", end - target)) { > + if (skip_prefix(target, "HEAD", &end) && (!*end || *end == '\n')) { > /* HEAD is relative. Resolve it to the right reflog entry. */ > strbuf_addstr(&cb->buf, > find_unique_abbrev(nsha1, DEFAULT_ABBREV)); > return 1; > } Yeah, I think parsing left-to-right like this makes things much more obvious. And regarding scanning HEAD twice, I think we already do that (we find the trailing newline first in the current code). Though I agree that is absurd premature optimization. > + for (end = target; *end && *end != '\n'; end++) > + ; This loop (which I know you just moved, not wrote) is basically strchrnul, isn't it? That might be more readable. -Peff ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] wt-status: use strncmp() for length-limited string comparison 2015-11-25 9:15 ` Jeff King @ 2015-11-25 10:29 ` Matthieu Moy 2015-11-25 14:10 ` [PATCH v2] wt-status: correct and simplify check for detached HEAD René Scharfe 1 sibling, 0 replies; 8+ messages in thread From: Matthieu Moy @ 2015-11-25 10:29 UTC (permalink / raw) To: Jeff King; +Cc: René Scharfe, Git List, Junio C Hamano Jeff King <peff@peff.net> writes: >> diff --git a/wt-status.c b/wt-status.c >> index 435fc28..96a731e 100644 >> --- a/wt-status.c >> +++ b/wt-status.c >> @@ -1317,14 +1317,14 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1, >> target += strlen(" to "); >> strbuf_reset(&cb->buf); >> hashcpy(cb->nsha1, nsha1); >> - for (end = target; *end && *end != '\n'; end++) >> - ; >> - if (!memcmp(target, "HEAD", end - target)) { >> + if (skip_prefix(target, "HEAD", &end) && (!*end || *end == '\n')) { >> /* HEAD is relative. Resolve it to the right reflog entry. */ >> strbuf_addstr(&cb->buf, >> find_unique_abbrev(nsha1, DEFAULT_ABBREV)); >> return 1; >> } > > Yeah, I think parsing left-to-right like this makes things much more > obvious. Agreed. >> + for (end = target; *end && *end != '\n'; end++) >> + ; > > This loop (which I know you just moved, not wrote) is basically > strchrnul, isn't it? That might be more readable. Agreed too. -- Matthieu Moy http://www-verimag.imag.fr/~moy/ ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v2] wt-status: correct and simplify check for detached HEAD 2015-11-25 9:15 ` Jeff King 2015-11-25 10:29 ` Matthieu Moy @ 2015-11-25 14:10 ` René Scharfe 2015-11-25 16:21 ` Matthieu Moy 2015-11-28 17:31 ` Jeff King 1 sibling, 2 replies; 8+ messages in thread From: René Scharfe @ 2015-11-25 14:10 UTC (permalink / raw) To: Jeff King; +Cc: Git List, Junio C Hamano, Matthieu Moy, Duy Nguyen If a branch name is longer than four characters then memcmp() reads over the end of the static string "HEAD". This causes the following test failures with AddressSanitizer: t3203-branch-output.sh (Wstat: 256 Tests: 18 Failed: 4) Failed tests: 12, 15-17 Non-zero exit status: 1 t3412-rebase-root.sh (Wstat: 256 Tests: 31 Failed: 3) Failed tests: 28-29, 31 Non-zero exit status: 1 t3507-cherry-pick-conflict.sh (Wstat: 256 Tests: 31 Failed: 4) Failed tests: 14, 29-31 Non-zero exit status: 1 t3510-cherry-pick-sequence.sh (Wstat: 256 Tests: 39 Failed: 14) Failed tests: 17, 22-26, 28-30, 34-35, 37-39 Non-zero exit status: 1 t3420-rebase-autostash.sh (Wstat: 256 Tests: 28 Failed: 4) Failed tests: 24-27 Non-zero exit status: 1 t3404-rebase-interactive.sh (Wstat: 256 Tests: 91 Failed: 57) Failed tests: 17, 19, 21-42, 44, 46-74, 77, 81-82 Non-zero exit status: 1 t3900-i18n-commit.sh (Wstat: 256 Tests: 34 Failed: 1) Failed test: 34 Non-zero exit status: 1 t5407-post-rewrite-hook.sh (Wstat: 256 Tests: 14 Failed: 6) Failed tests: 9-14 Non-zero exit status: 1 t7001-mv.sh (Wstat: 256 Tests: 46 Failed: 5) Failed tests: 39-43 Non-zero exit status: 1 t7509-commit.sh (Wstat: 256 Tests: 12 Failed: 2) Failed tests: 11-12 Non-zero exit status: 1 t7512-status-help.sh (Wstat: 256 Tests: 39 Failed: 35) Failed tests: 5-39 Non-zero exit status: 1 t6030-bisect-porcelain.sh (Wstat: 256 Tests: 70 Failed: 1) Failed test: 13 Non-zero exit status: 1 And if a branch is named "H", "HE", or "HEA" then the current if clause erroneously considers it as matching "HEAD" because it only compares up to the end of the branch name. Fix that by doing the comparison using strcmp() and only after the branch name is extracted. This way neither too less nor too many characters are checked. While at it call strchrnul() to find the end of the branch name instead of open-coding it. Helped-by: Jeff King <peff@peff.net> Signed-off-by: Rene Scharfe <l.s.r@web.de> --- We can be more careful when parsing -- or avoid parsing and backtrack if we found "HEAD" after all. The latter is simpler, and string parsing is tricky enough that we better take such opportunities to simplify the code.. Changes since v1: * strcmp() instead of strncmp() * strchrnul() (unrelated cleanup) * adjusted subject (and commit message) wt-status.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/wt-status.c b/wt-status.c index 435fc28..ced53dd 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1317,15 +1317,14 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1, target += strlen(" to "); strbuf_reset(&cb->buf); hashcpy(cb->nsha1, nsha1); - for (end = target; *end && *end != '\n'; end++) - ; - if (!memcmp(target, "HEAD", end - target)) { + end = strchrnul(target, '\n'); + strbuf_add(&cb->buf, target, end - target); + if (!strcmp(cb->buf.buf, "HEAD")) { /* HEAD is relative. Resolve it to the right reflog entry. */ + strbuf_reset(&cb->buf); strbuf_addstr(&cb->buf, find_unique_abbrev(nsha1, DEFAULT_ABBREV)); - return 1; } - strbuf_add(&cb->buf, target, end - target); return 1; } -- 2.6.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2] wt-status: correct and simplify check for detached HEAD 2015-11-25 14:10 ` [PATCH v2] wt-status: correct and simplify check for detached HEAD René Scharfe @ 2015-11-25 16:21 ` Matthieu Moy 2015-11-28 17:31 ` Jeff King 1 sibling, 0 replies; 8+ messages in thread From: Matthieu Moy @ 2015-11-25 16:21 UTC (permalink / raw) To: René Scharfe; +Cc: Jeff King, Git List, Junio C Hamano, Duy Nguyen René Scharfe <l.s.r@web.de> writes: > diff --git a/wt-status.c b/wt-status.c > index 435fc28..ced53dd 100644 > --- a/wt-status.c > +++ b/wt-status.c > @@ -1317,15 +1317,14 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1, > target += strlen(" to "); > strbuf_reset(&cb->buf); > hashcpy(cb->nsha1, nsha1); > - for (end = target; *end && *end != '\n'; end++) > - ; > - if (!memcmp(target, "HEAD", end - target)) { > + end = strchrnul(target, '\n'); > + strbuf_add(&cb->buf, target, end - target); > + if (!strcmp(cb->buf.buf, "HEAD")) { > /* HEAD is relative. Resolve it to the right reflog entry. */ > + strbuf_reset(&cb->buf); > strbuf_addstr(&cb->buf, > find_unique_abbrev(nsha1, DEFAULT_ABBREV)); > - return 1; > } > - strbuf_add(&cb->buf, target, end - target); > return 1; > } Indeed, the code is much simpler like this than with the previous attempts. Looks all right to me. -- Matthieu Moy http://www-verimag.imag.fr/~moy/ ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] wt-status: correct and simplify check for detached HEAD 2015-11-25 14:10 ` [PATCH v2] wt-status: correct and simplify check for detached HEAD René Scharfe 2015-11-25 16:21 ` Matthieu Moy @ 2015-11-28 17:31 ` Jeff King 1 sibling, 0 replies; 8+ messages in thread From: Jeff King @ 2015-11-28 17:31 UTC (permalink / raw) To: René Scharfe; +Cc: Git List, Junio C Hamano, Matthieu Moy, Duy Nguyen On Wed, Nov 25, 2015 at 03:10:18PM +0100, René Scharfe wrote: > Fix that by doing the comparison using strcmp() and only after the > branch name is extracted. This way neither too less nor too many > characters are checked. While at it call strchrnul() to find the end > of the branch name instead of open-coding it. > > Helped-by: Jeff King <peff@peff.net> > Signed-off-by: Rene Scharfe <l.s.r@web.de> > --- > We can be more careful when parsing -- or avoid parsing and backtrack > if we found "HEAD" after all. The latter is simpler, and string > parsing is tricky enough that we better take such opportunities to > simplify the code.. Yeah, I think this is nicer. We end up allocating either way anyway, so the only extra effort is copy the string. -Peff ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2015-11-28 17:32 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2015-11-06 22:47 [PATCH] wt-status: use strncmp() for length-limited string comparison René Scharfe 2015-11-24 21:36 ` Jeff King 2015-11-25 2:16 ` René Scharfe 2015-11-25 9:15 ` Jeff King 2015-11-25 10:29 ` Matthieu Moy 2015-11-25 14:10 ` [PATCH v2] wt-status: correct and simplify check for detached HEAD René Scharfe 2015-11-25 16:21 ` Matthieu Moy 2015-11-28 17:31 ` Jeff King
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.