diff mbox series

[3/3] builtin/checkout--worker: memset struct to avoid MSAN complaints

Message ID cd1e1f6985c77d21ec869e53dc5eb79673caf491.1623343713.git.gitgitgadget@gmail.com (mailing list archive)
State Superseded
Headers show
Series Fix uninitialised reads found with MSAN | expand

Commit Message

Andrzej Hunt June 10, 2021, 4:48 p.m. UTC
From: Andrzej Hunt <ajrhunt@google.com>

report_result() sends a struct to the parent process, but that struct
contains unintialised padding bytes. Running this code under MSAN
rightly triggers a warning - but we also don't care about this warning
because we control the receiving code, and we therefore know that those
padding bytes won't be read on the receiving end. Therefore we add a
memset to convince MSAN that this memory is safe to read - but only
when building with MSAN to avoid this cost in normal usage.

Interestingly, in the error-case branch, we only try to copy the first
two members of pc_item_result, by copying only PC_ITEM_RESULT_BASE_SIZE
bytes. However PC_ITEM_RESULT_BASE_SIZE is defined as
'offsetof(the_last_member)', which means that we're copying padding bytes
after the end of the second last member. We could avoid doing this by
redefining PC_ITEM_RESULT_BASE_SIZE as
'offsetof(second_last_member) + sizeof(second_last_member)', but there's
no huge benefit to doing so (and our memset hack silences the MSAN
warning in this scenario either way).

MSAN output from t2080 (partially interleaved due to the
parallel work :) ):

Uninitialized bytes in __interceptor_write at offset 12 inside [0x7fff37d83408, 160)
==23279==WARNING: MemorySanitizer: use-of-uninitialized-value
Uninitialized bytes in __interceptor_write at offset 12 inside [0x7ffdb8a07ec8, 160)
==23280==WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0xd5ac28 in xwrite /home/ahunt/git/git/wrapper.c:256:8
    #1 0xd5b327 in write_in_full /home/ahunt/git/git/wrapper.c:311:21
    #2 0xb0a8c4 in do_packet_write /home/ahunt/git/git/pkt-line.c:221:6
    #3 0xb0a5fd in packet_write /home/ahunt/git/git/pkt-line.c:242:6
    #4 0x4f7441 in report_result /home/ahunt/git/git/builtin/checkout--worker.c:69:2
    #5 0x4f6be6 in worker_loop /home/ahunt/git/git/builtin/checkout--worker.c:100:3
    #6 0x4f68d3 in cmd_checkout__worker /home/ahunt/git/git/builtin/checkout--worker.c:143:2
    #7 0x4a1e76 in run_builtin /home/ahunt/git/git/git.c:461:11
    #8 0x49e1e7 in handle_builtin /home/ahunt/git/git/git.c:714:3
    #9 0x4a0c08 in run_argv /home/ahunt/git/git/git.c:781:4
    #10 0x49d5a8 in cmd_main /home/ahunt/git/git/git.c:912:19
    #11 0x7974da in main /home/ahunt/git/git/common-main.c:52:11
    #12 0x7f8778114349 in __libc_start_main (/lib64/libc.so.6+0x24349)
    #13 0x421bd9 in _start /home/abuild/rpmbuild/BUILD/glibc-2.26/csu/../sysdeps/x86_64/start.S:120

  Uninitialized value was created by an allocation of 'res' in the stack frame of function 'report_result'
    #0 0x4f72c0 in report_result /home/ahunt/git/git/builtin/checkout--worker.c:55

SUMMARY: MemorySanitizer: use-of-uninitialized-value /home/ahunt/git/git/wrapper.c:256:8 in xwrite
Exiting
    #0 0xd5ac28 in xwrite /home/ahunt/git/git/wrapper.c:256:8
    #1 0xd5b327 in write_in_full /home/ahunt/git/git/wrapper.c:311:21
    #2 0xb0a8c4 in do_packet_write /home/ahunt/git/git/pkt-line.c:221:6
    #3 0xb0a5fd in packet_write /home/ahunt/git/git/pkt-line.c:242:6
    #4 0x4f7441 in report_result /home/ahunt/git/git/builtin/checkout--worker.c:69:2
    #5 0x4f6be6 in worker_loop /home/ahunt/git/git/builtin/checkout--worker.c:100:3
    #6 0x4f68d3 in cmd_checkout__worker /home/ahunt/git/git/builtin/checkout--worker.c:143:2
    #7 0x4a1e76 in run_builtin /home/ahunt/git/git/git.c:461:11
    #8 0x49e1e7 in handle_builtin /home/ahunt/git/git/git.c:714:3
    #9 0x4a0c08 in run_argv /home/ahunt/git/git/git.c:781:4
    #10 0x49d5a8 in cmd_main /home/ahunt/git/git/git.c:912:19
    #11 0x7974da in main /home/ahunt/git/git/common-main.c:52:11
    #12 0x7f2749a0e349 in __libc_start_main (/lib64/libc.so.6+0x24349)
    #13 0x421bd9 in _start /home/abuild/rpmbuild/BUILD/glibc-2.26/csu/../sysdeps/x86_64/start.S:120

  Uninitialized value was created by an allocation of 'res' in the stack frame of function 'report_result'
    #0 0x4f72c0 in report_result /home/ahunt/git/git/builtin/checkout--worker.c:55

SUMMARY: MemorySanitizer: use-of-uninitialized-value /home/ahunt/git/git/wrapper.c:256:8 in xwrite

Signed-off-by: Andrzej Hunt <andrzej@ahunt.org>
---
 builtin/checkout--worker.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

Comments

Chris Torek June 11, 2021, 4:43 a.m. UTC | #1
On Thu, Jun 10, 2021 at 9:49 AM Andrzej Hunt via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> [snip] Therefore we add a
> memset to convince MSAN that this memory is safe to read - but only
> when building with MSAN to avoid this cost in normal usage.

It does not seem likely to be that expensive, and would definitely
be shorter without all the `#if` testing:

> diff --git a/builtin/checkout--worker.c b/builtin/checkout--worker.c
> index 289a9b8f89d0..02fa5285988f 100644
> --- a/builtin/checkout--worker.c
> +++ b/builtin/checkout--worker.c
> @@ -56,6 +56,17 @@ static void report_result(struct parallel_checkout_item *pc_item)
>         struct pc_item_result res;

This could just have `= { 0 }` added.

In any case, this and all the others in this series look good to me.

Chris
Junio C Hamano June 11, 2021, 6:28 a.m. UTC | #2
Chris Torek <chris.torek@gmail.com> writes:

> On Thu, Jun 10, 2021 at 9:49 AM Andrzej Hunt via GitGitGadget
> <gitgitgadget@gmail.com> wrote:
>> [snip] Therefore we add a
>> memset to convince MSAN that this memory is safe to read - but only
>> when building with MSAN to avoid this cost in normal usage.
>
> It does not seem likely to be that expensive, and would definitely
> be shorter without all the `#if` testing:
>
>> diff --git a/builtin/checkout--worker.c b/builtin/checkout--worker.c
>> index 289a9b8f89d0..02fa5285988f 100644
>> --- a/builtin/checkout--worker.c
>> +++ b/builtin/checkout--worker.c
>> @@ -56,6 +56,17 @@ static void report_result(struct parallel_checkout_item *pc_item)
>>         struct pc_item_result res;
>
> This could just have `= { 0 }` added.

I'd prefer that very much more than the #if testing, within which //
comments that we do not want in our codebase are enclosed.

Thanks.
Andrzej Hunt June 11, 2021, 3:37 p.m. UTC | #3
On 11/06/2021 08:28, Junio C Hamano wrote:
> Chris Torek <chris.torek@gmail.com> writes:
> 
>> On Thu, Jun 10, 2021 at 9:49 AM Andrzej Hunt via GitGitGadget
>> <gitgitgadget@gmail.com> wrote:
>>> [snip] Therefore we add a
>>> memset to convince MSAN that this memory is safe to read - but only
>>> when building with MSAN to avoid this cost in normal usage.
>>
>> It does not seem likely to be that expensive, and would definitely
>> be shorter without all the `#if` testing:
>>
>>> diff --git a/builtin/checkout--worker.c b/builtin/checkout--worker.c
>>> index 289a9b8f89d0..02fa5285988f 100644
>>> --- a/builtin/checkout--worker.c
>>> +++ b/builtin/checkout--worker.c
>>> @@ -56,6 +56,17 @@ static void report_result(struct parallel_checkout_item *pc_item)
>>>          struct pc_item_result res;
>>
>> This could just have `= { 0 }` added.
> 
> I'd prefer that very much more than the #if testing, within which //
> comments that we do not want in our codebase are enclosed.


I'll fix this for V2 - thanks Chris and Junio!

(At the time I wasn't aware that this would include all members and 
padding, but I've learned more since reading the clang developer's 
discussion around padding and brace intialisation :) : 
https://reviews.llvm.org/D61280 . )
Junio C Hamano June 14, 2021, 1:04 a.m. UTC | #4
Andrzej Hunt <andrzej@ahunt.org> writes:

> (At the time I wasn't aware that this would include all members and
> padding, but I've learned more since reading the clang developer's 
> discussion around padding and brace intialisation :) :
> https://reviews.llvm.org/D61280 . )

Thanks for a pointer ;-)
diff mbox series

Patch

diff --git a/builtin/checkout--worker.c b/builtin/checkout--worker.c
index 289a9b8f89d0..02fa5285988f 100644
--- a/builtin/checkout--worker.c
+++ b/builtin/checkout--worker.c
@@ -56,6 +56,17 @@  static void report_result(struct parallel_checkout_item *pc_item)
 	struct pc_item_result res;
 	size_t size;
 
+#if defined(__has_feature)
+#  if __has_feature(memory_sanitizer)
+	// MSAN workaround: res contains padding bytes, which will remain
+	// permanently unintialised. Later, we read all of res in order to send
+	// it to the parent process - and MSAN (rightly) complains that we're
+	// reading those unintialised padding bytes. By memset'ing res we
+	// guarantee that there are no uninitialised bytes.
+	memset(&res, 0, sizeof(res));
+#endif
+#endif
+
 	res.id = pc_item->id;
 	res.status = pc_item->status;