-
Notifications
You must be signed in to change notification settings - Fork 142
Initialize a few uninitialized variables #1888
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
b5e98d5
575b4b2
b990192
d630e95
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -925,7 +925,7 @@ static int fsck_commit(const struct object_id *oid, | |
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the Git mailing list, Jeff King wrote (reply to this): On Thu, Mar 27, 2025 at 12:43:47PM +0000, Johannes Schindelin via GitGitGadget wrote:
> From: Johannes Schindelin <[email protected]>
>
> In `fsck_commit()`, after counting the authors of a commit, we set the
> `err` variable either when there was no author, or when there were more
> than two authors recorded. Then we access the `err` variable to figure
> out whether we should return early. But if there was exactly one author,
> that variable is still uninitialized.
>
> Let's just initialize the variable.
>
> This issue was pointed out by CodeQL.
Hmm, I'd think we would hit this case all the time, since commits
generally have one author. But I think it's another false positive.
The code in question is this:
author_count = 0;
while (buffer < buffer_end && skip_prefix(buffer, "author ", &buffer)) {
author_count++;
err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
if (err)
return err;
}
if (author_count < 1)
err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line");
else if (author_count > 1)
err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines");
if (err)
return err;
So we set "err" as soon as we find _any_ author (when we check whether
it is properly formatted via fsck_ident). And author_count will not be
incremented if we did not find one. So either we must have assigned
the result of fsck_ident(), or we will hit the "author_count < 1" case
and assign there.
It's certainly confusing, though, since "err" gets used in so many
spots. I think the whole thing would be easier to understand if we had
tighter-scoped single use variables like this:
diff --git a/fsck.c b/fsck.c
index 9fc4c25ffd..ea72b3247d 100644
--- a/fsck.c
+++ b/fsck.c
@@ -925,7 +925,6 @@ static int fsck_commit(const struct object_id *oid,
{
struct object_id tree_oid, parent_oid;
unsigned author_count;
- int err;
const char *buffer_begin = buffer;
const char *buffer_end = buffer + size;
const char *p;
@@ -941,39 +940,44 @@ static int fsck_commit(const struct object_id *oid,
if (buffer >= buffer_end || !skip_prefix(buffer, "tree ", &buffer))
return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') {
- err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
+ int err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
if (err)
return err;
}
buffer = p + 1;
while (buffer < buffer_end && skip_prefix(buffer, "parent ", &buffer)) {
if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n') {
- err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
+ int err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
if (err)
return err;
}
buffer = p + 1;
}
author_count = 0;
while (buffer < buffer_end && skip_prefix(buffer, "author ", &buffer)) {
+ int err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
+ if (err)
+ return err;
author_count++;
- err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
+ }
+ if (author_count < 1) {
+ int err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line");
+ if (err)
+ return err;
+ } else if (author_count > 1) {
+ int err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines");
if (err)
return err;
}
- if (author_count < 1)
- err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line");
- else if (author_count > 1)
- err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines");
- if (err)
- return err;
if (buffer >= buffer_end || !skip_prefix(buffer, "committer ", &buffer))
return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_COMMITTER, "invalid format - expected 'committer' line");
- err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
- if (err)
- return err;
+ else {
+ int err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
+ if (err)
+ return err;
+ }
if (memchr(buffer_begin, '\0', size)) {
- err = report(options, oid, OBJ_COMMIT, FSCK_MSG_NUL_IN_COMMIT,
+ int err = report(options, oid, OBJ_COMMIT, FSCK_MSG_NUL_IN_COMMIT,
"NUL byte in the commit object body");
if (err)
return err;
And then it is obvious that the general pattern is to propagate "err"
from individual calls (and the ones that do not stick out like sore
thumbs; are those bugs where we should keep going if the user set those
message types to warn/ignore?).
You could even wrap the pattern in a macro, though perhaps that is
getting too magical. The resulting logic is easier to follow, though, if
you can look past the macro:
diff --git a/fsck.c b/fsck.c
index ea72b3247d..8c7ac3c448 100644
--- a/fsck.c
+++ b/fsck.c
@@ -919,6 +919,12 @@ static int fsck_ident(const char **ident,
return 0;
}
+#define MAYBE_RETURN(x) do { \
+ int err = (x); \
+ if (err) \
+ return err; \
+} while (0)
+
static int fsck_commit(const struct object_id *oid,
const char *buffer, unsigned long size,
struct fsck_options *options)
@@ -939,49 +945,30 @@ static int fsck_commit(const struct object_id *oid,
if (buffer >= buffer_end || !skip_prefix(buffer, "tree ", &buffer))
return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
- if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') {
- int err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
- if (err)
- return err;
- }
+ if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n')
+ MAYBE_RETURN(report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1"));
buffer = p + 1;
while (buffer < buffer_end && skip_prefix(buffer, "parent ", &buffer)) {
- if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n') {
- int err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
- if (err)
- return err;
- }
+ if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n')
+ MAYBE_RETURN(report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1"));
buffer = p + 1;
}
author_count = 0;
while (buffer < buffer_end && skip_prefix(buffer, "author ", &buffer)) {
- int err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
- if (err)
- return err;
+ MAYBE_RETURN(fsck_ident(&buffer, oid, OBJ_COMMIT, options));
author_count++;
}
- if (author_count < 1) {
- int err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line");
- if (err)
- return err;
- } else if (author_count > 1) {
- int err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines");
- if (err)
- return err;
- }
+ if (author_count < 1)
+ MAYBE_RETURN(report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line"));
+ else if (author_count > 1)
+ MAYBE_RETURN(report(options, oid, OBJ_COMMIT, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines"));
if (buffer >= buffer_end || !skip_prefix(buffer, "committer ", &buffer))
return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_COMMITTER, "invalid format - expected 'committer' line");
- else {
- int err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
- if (err)
- return err;
- }
- if (memchr(buffer_begin, '\0', size)) {
- int err = report(options, oid, OBJ_COMMIT, FSCK_MSG_NUL_IN_COMMIT,
- "NUL byte in the commit object body");
- if (err)
- return err;
- }
+ else
+ MAYBE_RETURN(fsck_ident(&buffer, oid, OBJ_COMMIT, options));
+
+ if (memchr(buffer_begin, '\0', size))
+ MAYBE_RETURN(report(options, oid, OBJ_COMMIT, FSCK_MSG_NUL_IN_COMMIT, "NUL byte in the commit object body"));
return 0;
}
I'd suspect that just the first patch above would fix the CodeQL issue.
It's certainly a larger diff, but IMHO the result is less confusing for
humans, too.
-Peff |
||
struct object_id tree_oid, parent_oid; | ||
unsigned author_count; | ||
int err; | ||
int err = 0; | ||
const char *buffer_begin = buffer; | ||
const char *buffer_end = buffer + size; | ||
const char *p; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,7 +29,7 @@ static int load_pack_mtimes_file(char *mtimes_file, | |
int fd, ret = 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the Git mailing list, Taylor Blau wrote (reply to this): On Thu, Mar 27, 2025 at 12:43:49PM +0000, Johannes Schindelin via GitGitGadget wrote:
> From: Johannes Schindelin <[email protected]>
>
> The `mtimes_size` variable is uninitialzed when the function errors out,
> yet its value is assigned to another variable. Let's just initialize it.
>
> Signed-off-by: Johannes Schindelin <[email protected]>
> ---
> pack-mtimes.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/pack-mtimes.c b/pack-mtimes.c
> index cdf30b8d2b0..c1f531d45a0 100644
> --- a/pack-mtimes.c
> +++ b/pack-mtimes.c
> @@ -29,7 +29,7 @@ static int load_pack_mtimes_file(char *mtimes_file,
> int fd, ret = 0;
> struct stat st;
> uint32_t *data = NULL;
> - size_t mtimes_size, expected_size;
> + size_t mtimes_size = 0, expected_size;
Hmm. This one follows an identical line of reasoning as in my previous
response in the thread. So I think this one is likewise unnecessary
(though not harmful, and certainly useful if it appeases static analysis
tools, etc).
Thanks,
Taylor |
||
struct stat st; | ||
uint32_t *data = NULL; | ||
size_t mtimes_size, expected_size; | ||
size_t mtimes_size = 0, expected_size; | ||
struct mtimes_header header; | ||
|
||
fd = git_open(mtimes_file); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -208,7 +208,7 @@ static int load_revindex_from_disk(char *revindex_name, | |
int fd, ret = 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the Git mailing list, Taylor Blau wrote (reply to this): On Thu, Mar 27, 2025 at 12:43:48PM +0000, Johannes Schindelin via GitGitGadget wrote:
> From: Johannes Schindelin <[email protected]>
>
> The `revindex_size` value is uninitialized in case the function is
> erroring out, but we want to assign its value. Let's just initialize it.
>
> Signed-off-by: Johannes Schindelin <[email protected]>
> ---
> pack-revindex.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/pack-revindex.c b/pack-revindex.c
> index d3832478d99..3b007d771b3 100644
> --- a/pack-revindex.c
> +++ b/pack-revindex.c
> @@ -208,7 +208,7 @@ static int load_revindex_from_disk(char *revindex_name,
> int fd, ret = 0;
> struct stat st;
> void *data = NULL;
> - size_t revindex_size;
> + size_t revindex_size = 0;
I'm certainly not opposed to initializing variables proactively, but in
this particular case I don't think it's necessary.
We assign 'revindex_size' out to 'len_p' when we enter the cleanup
routine label if 'ret' is zero. We'll use 'revindex_size' in the same
label to munmap() when 'ret' is non-zero, but only if 'data' is also
initialized.
So there are two conditions where we'll enter the cleanup label before
assigning 'revindex_size', when git_open() returns a negative value, or
fstat()ing the descriptor that git_open() gave us returns a non-zero
value. In both of those cases, ret is non-zero (it is assigned to 1 and
the return value of error_errno() in those cases, respectively). Since
'data' is also NULL here, this function will terminate without using
the uninitialized 'revindex_size'.
If both of those work (i.e., we opened the file and fstat()ed it
successfully), then we'll have revindex_size initialized to st.st_size
(really the result of calling xsize_t() on it). There are two sanity
checks on the size, both of which happen before we have mmap()ed the
file, and both sanity checks set 'ret' to a non-zero value upon failure.
So by the time we '*len_p = revindex_size' it is guaranteed to be
initialized and just junk bytes on the stack.
Did this trigger a warning from a static analyzer or something? If so,
I'm happy to take this patch to appease it. Perhaps that it what's going
on since I recall you mentioning that you were working on enabling
CodeQL in Microsoft's fork of Git.
But if not I might suggest dropping this patch for the reasons above.
Thanks,
Taylor |
||
struct stat st; | ||
void *data = NULL; | ||
size_t revindex_size; | ||
size_t revindex_size = 0; | ||
struct revindex_header *hdr; | ||
|
||
if (git_env_bool(GIT_TEST_REV_INDEX_DIE_ON_DISK, 0)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the Git mailing list, Jeff King wrote (reply to this):