Bug 1655642

Summary: valgrind wcsncmp reports "Conditional jump or move depends on uninitialised value" on glibc strcmp-avx2.S:113
Product: Red Hat Enterprise Linux 8 Reporter: Mark Wielaard <mjw>
Component: valgrindAssignee: Mark Wielaard <mjw>
Status: CLOSED CURRENTRELEASE QA Contact: Alexandra Petlanová Hájková <ahajkova>
Severity: medium Docs Contact:
Priority: unspecified    
Version: 8.0CC: ahajkova, jakub, ohudlick
Target Milestone: rc   
Target Release: 8.0   
Hardware: x86_64   
OS: Linux   
Whiteboard:
Fixed In Version: valgrind-3.14.0-5.el8 Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: 1645971 Environment:
Last Closed: 2019-06-14 01:34:04 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On: 1645971    
Bug Blocks:    

Description Mark Wielaard 2018-12-03 15:40:04 UTC
+++ This bug was initially created as a clone of Bug #1645971 +++

Description of problem:

Valgrind is reporting what I suspect is a false positive:

"Conditional jump or move depends on uninitialised value" on
glibc strcmp-avx2.S:113

I've created a test program attached that generates the report using wcsncmp.  It appears to be length dependant on the underlying memory that is allocated.
This did not appear in versions of Fedora prior to 29.

The output of the test program is below.

[root@localhost net]# gcc test.c
[root@localhost net]# valgrind ./a.out
==26675== Memcheck, a memory error detector
==26675== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==26675== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==26675== Command: ./a.out
==26675== 


first test case (8 wchar_t), no complaints from valgrind
test case 1 match


second test case (6 wchar_t), valgrind complains
==26675== Conditional jump or move depends on uninitialised value(s)
==26675==    at 0x49C4E59: __wcsncmp_avx2 (strcmp-avx2.S:113)
==26675==    by 0x40121B: main (in /home/russell/net/a.out)
==26675== 
test case 2 match
==26675== 
==26675== HEAP SUMMARY:
==26675==     in use at exit: 0 bytes in 0 blocks
==26675==   total heap usage: 3 allocs, 3 frees, 1,080 bytes allocated
==26675== 
==26675== All heap blocks were freed -- no leaks are possible
==26675== 
==26675== For counts of detected and suppressed errors, rerun with: -v
==26675== Use --track-origins=yes to see where uninitialised values come from
==26675== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)


Version-Release number of selected component (if applicable):

valgrind-3.14.0-1.fc29.x86_64

--- Additional comment from Mark Wielaard on 2018-11-05 07:59:00 EST ---

This probably comes from the following glibc commit glibc-2.27.9000-436-g1457016:

commit 1457016337072d1b6739f571846b619596990cb7
Author: Leonardo Sandoval <leonardo.sandoval.gonzalez.com>
Date:   Thu May 3 11:09:30 2018 -0500

    x86-64: Optimize strcmp/wcscmp and strncmp/wcsncmp with AVX2

strcmp, ecscmp and strncmp have overrides in valgrind shared/vg_replace_strmem.c.
But wcsncmp doesn't.

Comment 1 Mark Wielaard 2018-12-03 15:54:08 UTC
This has an easy reproducer on any x86_64 with avx2 setup. Any program using wcsncmp will produce the issue. The upstream fix contains this test program:

$ cat memcheck/tests/wcs.c 
// Uses various wchar_t * functions that have hand written SSE assembly
// implementations in glibc. wcslen, wcscpy, wcscmp, wcsrchr, wcschr.

#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>

int main(int argc, char **argv)
{
  wchar_t a[] = L"The spazzy orange tiger jumped over the tawny jaguar.";
  wchar_t *b, *c;
  wchar_t *d, *e;

  size_t l = wcslen (a);
  fprintf (stderr, "wcslen: %zd\n", l); // wcslen: 53

  b = (wchar_t *) malloc((l + 1) * sizeof (wchar_t));
  c = wcscpy (b, a);

  fprintf (stderr, "wcscmp equal: %d\n", wcscmp (a, b)); // wcscmp equal: 0

  d = wcsrchr (a, L'd');
  e = wcschr (a, L'd');

  fprintf (stderr, "wcsrchr == wcschr: %d\n", d == e); // wcsrchr == wcschr: 1

  free (c); // b == c
  return 0;
}

$ cp valgrind/memcheck/tests/wcs.c  .
$ gcc -g -o wcs wcs.c 
$ valgrind -q ./wcs 
wcslen: 53
wcscmp equal: 0
==10843== Invalid read of size 32
==10843==    at 0x4F9CD43: __wcsncmp_avx2 (in /usr/lib64/libc-2.28.so)
==10843==    by 0x400A33: main (wcs.c:22)
==10843==  Address 0x5201120 is 8 bytes after a block of size 216 alloc'd
==10843==    at 0x4C30E4B: malloc (vg_replace_malloc.c:299)
==10843==    by 0x4009CA: main (wcs.c:18)
==10843== 
==10843== Invalid read of size 32
==10843==    at 0x4F9CD61: __wcsncmp_avx2 (in /usr/lib64/libc-2.28.so)
==10843==    by 0x400A33: main (wcs.c:22)
==10843==  Address 0x5201140 is 32 bytes before an unallocated block of size 4,193,920 in arena "client"
==10843== 
==10843== Invalid read of size 32
==10843==    at 0x4F9CD66: __wcsncmp_avx2 (in /usr/lib64/libc-2.28.so)
==10843==    by 0x400A33: main (wcs.c:22)
==10843==  Address 0x5201160 is 0 bytes inside an unallocated block of size 4,193,920 in arena "client"
==10843== 
==10843== Conditional jump or move depends on uninitialised value(s)
==10843==    at 0x4F9CD9E: __wcsncmp_avx2 (in /usr/lib64/libc-2.28.so)
==10843==    by 0x400A33: main (wcs.c:22)
==10843== 
wcsncmp equal: 0
wcsrchr == wcschr: 1

With the fix:

$ valgrind -q ./wcs
wcslen: 53
wcscmp equal: 0
wcsncmp equal: 0
wcsrchr == wcschr: 1