MantisBT - CMake
View Issue Details
0015944CMakeCMakepublic2016-01-29 13:102016-06-10 14:21
hanno 
Brad King 
normalminoralways
closedfixed 
LinuxGentoo
CMake 3.3.2 
CMake 3.5CMake 3.5 
0015944: Use after free in regexp functionality
The attached file will cause a use after free error in cmake. This is a reduced example, I originally discovered this by using a version of cmake built with address sanitizer.

This bug can also be seen by using cmake with valgrind (but the asan output is more detailed).
1. run "cmake ." in a directory with the attached file. cmake must either be compiled with address sanitizer or run with valgrind.
Here's the significant part of the asan error:

==10481==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000014998 at pc 0x7f7f52711590 bp 0x7ffedc5d4690 sp 0x7ffedc5d4660
READ of size 2 at 0x603000014998 thread T0
    #0 0x7f7f5271158f in __interceptor_strchr (/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.3/libasan.so.1+0x3458f)
    #1 0x564b27c3891b in strchr /usr/include/string.h:226
    0000002 0x564b27c3891b in cmsys::RegularExpression::find(char const*) /var/tmp/portage/dev-util/cmake-3.4.3/work/cmake-3.4.3/Source/kwsys/RegularExpression.cxx:928
    0000003 0x564b278c9f7e in cmConditionEvaluator::HandleLevel2(std::list<cmExpandedCommandArgument, std::allocator<cmExpandedCommandArgument> >&, std::string&, cmake::MessageType&) /var/tmp/portage/dev-util/cmake-3.4.3/work/cmake-3.4.3/Source/cmConditionEvaluator.cxx:608
    0000004 0x564b278decd8 in cmConditionEvaluator::IsTrue(std::vector<cmExpandedCommandArgument, std::allocator<cmExpandedCommandArgument> > const&, std::string&, cmake::MessageType&) /var/tmp/portage/dev-util/cmake-3.4.3/work/cmake-3.4.3/Source/cmConditionEvaluator.cxx:78
    0000005 0x564b278df3a0 in cmIfCommand::InvokeInitialPass(std::vector<cmListFileArgument, std::allocator<cmListFileArgument> > const&, cmExecutionStatus&) /var/tmp/portage/dev-util/cmake-3.4.3/work/cmake-3.4.3/Source/cmIfCommand.cxx:217
    0000006 0x564b276bb068 in cmMakefile::ExecuteCommand(cmListFileFunction const&, cmExecutionStatus&) /var/tmp/portage/dev-util/cmake-3.4.3/work/cmake-3.4.3/Source/cmMakefile.cxx:305
    0000007 0x564b276bb7d2 in cmMakefile::ReadListFile(cmListFile const&, std::string const&) /var/tmp/portage/dev-util/cmake-3.4.3/work/cmake-3.4.3/Source/cmMakefile.cxx:611
    0000008 0x564b276bd3a6 in cmMakefile::Configure() /var/tmp/portage/dev-util/cmake-3.4.3/work/cmake-3.4.3/Source/cmMakefile.cxx:1663
    #9 0x564b27aa5ec3 in cmGlobalGenerator::Configure() /var/tmp/portage/dev-util/cmake-3.4.3/work/cmake-3.4.3/Source/cmGlobalGenerator.cxx:1126
    0000010 0x564b27ac9241 in cmGlobalUnixMakefileGenerator3::Configure() /var/tmp/portage/dev-util/cmake-3.4.3/work/cmake-3.4.3/Source/cmGlobalUnixMakefileGenerator3.cxx:133
    #11 0x564b2779ed6b in cmake::ActualConfigure() /var/tmp/portage/dev-util/cmake-3.4.3/work/cmake-3.4.3/Source/cmake.cxx:1422
    0000012 0x564b277a0248 in cmake::Configure() /var/tmp/portage/dev-util/cmake-3.4.3/work/cmake-3.4.3/Source/cmake.cxx:1205
    0000013 0x564b277a51f7 in cmake::Run(std::vector<std::string, std::allocator<std::string> > const&, bool) /var/tmp/portage/dev-util/cmake-3.4.3/work/cmake-3.4.3/Source/cmake.cxx:1579
    0000014 0x564b2763f2f9 in do_cmake(int, char const* const*) /var/tmp/portage/dev-util/cmake-3.4.3/work/cmake-3.4.3/Source/cmakemain.cxx:330
    0000015 0x564b2763751a in main /var/tmp/portage/dev-util/cmake-3.4.3/work/cmake-3.4.3/Source/cmakemain.cxx:190
    0000016 0x7f7f511b062f in __libc_start_main (/lib64/libc.so.6+0x2062f)
    0000017 0x564b2763ce78 in _start (/usr/bin/cmake+0x163e78)

0x603000014998 is located 24 bytes inside of 26-byte region [0x603000014980,0x60300001499a)
freed by thread T0 here:
    #0 0x7f7f5273561f in operator delete(void*) (/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.3/libasan.so.1+0x5861f)
    #1 0x7f7f51b08c37 in std::string::assign(std::string const&) (/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/libstdc++.so.6+0xcec37)
    0000002 0x61c00000f87f (+0xf87f)
    0000003 0x7f7f51b09485 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/libstdc++.so.6+0xcf485)

previously allocated by thread T0 here:
    #0 0x7f7f5273511f in operator new(unsigned long) (/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.3/libasan.so.1+0x5811f)
    #1 0x7f7f51b076c8 in std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/libstdc++.so.6+0xcd6c8)
    0000002 0x7f7f51b076c8 in std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/libstdc++.so.6+0xcd6c8)
    0000003 0x564b28040147 in matchVariables (/usr/bin/cmake+0xb67147)
No tags attached.
txt CMakeLists.txt (36) 2016-01-29 13:10
https://public.kitware.com/Bug/file/5622/CMakeLists.txt
txt asan-error-cmake-uaf.txt (5,562) 2016-01-29 13:10
https://public.kitware.com/Bug/file/5623/asan-error-cmake-uaf.txt
txt valgrind-uaf-cmake.txt (10,243) 2016-01-29 13:11
https://public.kitware.com/Bug/file/5624/valgrind-uaf-cmake.txt
Issue History
2016-01-29 13:10hannoNew Issue
2016-01-29 13:10hannoFile Added: CMakeLists.txt
2016-01-29 13:10hannoFile Added: asan-error-cmake-uaf.txt
2016-01-29 13:11hannoFile Added: valgrind-uaf-cmake.txt
2016-01-29 13:40Brad KingNote Added: 0040367
2016-01-29 13:43hannoNote Added: 0040368
2016-01-29 13:43Brad KingNote Added: 0040369
2016-01-29 15:15Brad KingNote Added: 0040378
2016-01-30 17:56Gregor JasnyNote Added: 0040382
2016-01-31 06:09hannoNote Added: 0040383
2016-02-01 10:34Brad KingNote Added: 0040425
2016-02-01 10:35Brad KingAssigned To => Brad King
2016-02-01 10:35Brad KingStatusnew => resolved
2016-02-01 10:35Brad KingResolutionopen => fixed
2016-02-01 10:35Brad KingFixed in Version => CMake 3.5
2016-02-01 10:35Brad KingTarget Version => CMake 3.5
2016-02-02 03:56hannoNote Added: 0040426
2016-06-10 14:21Kitware RobotNote Added: 0041267
2016-06-10 14:21Kitware RobotStatusresolved => closed

Notes
(0040367)
Brad King   
2016-01-29 13:40   
From the CMakeLists.txt file:

> if("CMAKE_MATCH_COUNT" MATCHES "Y")

The MATCHES operation sets CMAKE_MATCH_COUNT so the value is disappearing while it is being matched.
(0040368)
hanno   
2016-01-29 13:43   
I forgot to mention that, but this comes actually from a real world example, it happens with mariadb's cmake build.
(0040369)
Brad King   
2016-01-29 13:43   
The code for MATCHES is here:

 https://cmake.org/gitweb?p=cmake.git;a=blob;f=Source/cmConditionEvaluator.cxx;hb=v3.4.3#l596 [^]

The cmMakefile::ClearMatches call goes here:

 https://cmake.org/gitweb?p=cmake.git;a=blob;f=Source/cmMakefile.cxx;hb=v3.4.3#l4491 [^]

That updates CMAKE_MATCH_COUNT and some other variables, which may invalidate the string that cmConditionEvaluator just saved in a "const char*".
(0040378)
Brad King   
2016-01-29 15:15   
Strangely I'm unable to reproduce this locally with either valgrind or address sanitizer. Please try the patch below. A full solution will require refactoring how the CMAKE_MATCH_ variables are populated, but we can work around this by storing the input string in our own buffer. I prefer not to do this in general because it will mean a lot of copying.

diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx
index 5330acd..30244fb 100644
--- a/Source/cmConditionEvaluator.cxx
+++ b/Source/cmConditionEvaluator.cxx
@@ -594,6 +594,12 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList &newArgs,
         IsKeyword("MATCHES", *argP1))
         {
         def = this->GetVariableOrString(*arg);
+ std::string buf;
+ if (cmHasLiteralPrefix(*arg, "CMAKE_MATCH_"))
+ {
+ buf = def;
+ def = buf.c_str();
+ }
         const char* rex = argP2->c_str();
         this->Makefile.ClearMatches();
         cmsys::RegularExpression regEntry;
(0040382)
Gregor Jasny   
2016-01-30 17:56   
During implementation of the regex explorer in the CMake GUI I also noticed that I have to store the input string somewhere because the regex class does not do so. But because it is in the kwsys namespace I was a bit hesitant to refactoring.
(0040383)
hanno   
2016-01-31 06:09   
The patch from comment 0040378 doesn't work for me, error message:

g++ -I/mnt/ram/cmake-3.4.3/Bootstrap.cmk -I/mnt/ram/cmake-3.4.3/Source -I/mnt/ram/cmake-3.4.3/Bootstrap.cmk -c /mnt/ram/cmake-3.4.3/Source/cmBootstrapCommands2.cxx -o cmBootstrapCommands2.o
In file included from /mnt/ram/cmake-3.4.3/Source/cmState.h:19:0,
                 from /mnt/ram/cmake-3.4.3/Source/cmListFileCache.h:17,
                 from /mnt/ram/cmake-3.4.3/Source/cmCommand.h:16,
                 from /mnt/ram/cmake-3.4.3/Source/cmConditionEvaluator.h:15,
                 from /mnt/ram/cmake-3.4.3/Source/cmConditionEvaluator.cxx:13,
                 from /mnt/ram/cmake-3.4.3/Source/cmBootstrapCommands2.cxx:17:
/mnt/ram/cmake-3.4.3/Source/cmAlgorithms.h: In instantiation of ‘bool cmHasLiteralPrefix(T, const char (&)[N]) [with T = cmExpandedCommandArgument; long unsigned int N = 13ul]’:
/mnt/ram/cmake-3.4.3/Source/cmConditionEvaluator.cxx:598:52: required from here
/mnt/ram/cmake-3.4.3/Source/cmAlgorithms.h:57:50: error: no matching function for call to ‘cmHasLiteralPrefixImpl(cmExpandedCommandArgument&, const char [13], long unsigned int)’
   return cmHasLiteralPrefixImpl(str1, str2, N - 1);
                                                  ^
/mnt/ram/cmake-3.4.3/Source/cmAlgorithms.h:57:50: note: candidates are:
/mnt/ram/cmake-3.4.3/Source/cmAlgorithms.h:17:13: note: bool cmHasLiteralPrefixImpl(const string&, const char*, size_t)
 inline bool cmHasLiteralPrefixImpl(const std::string &str1,
             ^
/mnt/ram/cmake-3.4.3/Source/cmAlgorithms.h:17:13: note: no known conversion for argument 1 from ‘cmExpandedCommandArgument’ to ‘const string& {aka const std::basic_string<char>&}’
/mnt/ram/cmake-3.4.3/Source/cmAlgorithms.h:24:13: note: bool cmHasLiteralPrefixImpl(const char*, const char*, size_t)
 inline bool cmHasLiteralPrefixImpl(const char* str1,
             ^
/mnt/ram/cmake-3.4.3/Source/cmAlgorithms.h:24:13: note: no known conversion for argument 1 from ‘cmExpandedCommandArgument’ to ‘const char*’
Makefile:132: recipe for target 'cmBootstrapCommands2.o' failed
gmake: *** [cmBootstrapCommands2.o] Error 1
(0040425)
Brad King   
2016-02-01 10:34   
I've appiled a revision of the patch from 0015944:0040378:

 cmConditionEvaluator: Fix matching of `CMAKE_MATCH_*` values
 https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=6ffc4323 [^]
(0040426)
hanno   
2016-02-02 03:56   
Thanks, I have tested the patch from git applied on cmake 3.4.3 and it seems to fix the issue.
(0041267)
Kitware Robot   
2016-06-10 14:21   
This issue tracker is no longer used. Further discussion of this issue may take place in the current CMake Issues page linked in the banner at the top of this page.