2014-08-06 14:20:46 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2023-01-28 10:16:34 +01:00
* Copyright ( C ) 2007 - 2023 Cppcheck team .
2014-08-06 14:20:46 +02:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2017-05-27 04:33:47 +02:00
2014-08-06 14:20:46 +02:00
# include "checkvaarg.h"
2022-01-27 19:03:20 +01:00
# include "errortypes.h"
2017-05-27 04:33:47 +02:00
# include "settings.h"
2023-01-27 08:18:32 +01:00
# include "fixture.h"
2014-08-06 14:20:46 +02:00
# include "tokenize.h"
2022-09-16 07:15:49 +02:00
# include <sstream> // IWYU pragma: keep
2014-08-06 14:20:46 +02:00
class TestVaarg : public TestFixture {
public :
TestVaarg ( ) : TestFixture ( " TestVaarg " ) { }
private :
2023-05-02 11:48:24 +02:00
const Settings settings = settingsBuilder ( ) . severity ( Severity : : warning ) . build ( ) ;
2015-10-07 18:33:57 +02:00
2021-11-29 07:34:39 +01:00
# define check(code) check_(code, __FILE__, __LINE__)
void check_ ( const char code [ ] , const char * file , int line ) {
2014-08-06 14:20:46 +02:00
// Clear the error buffer..
errout . str ( " " ) ;
// Tokenize..
Tokenizer tokenizer ( & settings , this ) ;
std : : istringstream istr ( code ) ;
2021-11-29 07:34:39 +01:00
ASSERT_LOC ( tokenizer . tokenize ( istr , " test.cpp " ) , file , line ) ;
2014-08-06 14:20:46 +02:00
// Check..
2023-08-18 12:03:50 +02:00
runChecks < CheckVaarg > ( tokenizer , this ) ;
2014-08-06 14:20:46 +02:00
}
2022-02-10 23:02:24 +01:00
void run ( ) override {
2014-08-06 14:20:46 +02:00
TEST_CASE ( wrongParameterTo_va_start ) ;
TEST_CASE ( referenceAs_va_start ) ;
TEST_CASE ( va_end_missing ) ;
TEST_CASE ( va_list_usedBeforeStarted ) ;
TEST_CASE ( va_start_subsequentCalls ) ;
2015-12-05 20:55:26 +01:00
TEST_CASE ( unknownFunctionScope ) ;
2014-08-06 14:20:46 +02:00
}
2014-11-20 14:20:09 +01:00
void wrongParameterTo_va_start ( ) {
2014-08-06 14:20:46 +02:00
check ( " void Format(char* szFormat, char* szBuffer, size_t nSize, ...) { \n "
" va_list arg_ptr; \n "
" va_start(arg_ptr, szFormat); \n "
" va_end(arg_ptr); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (warning) 'szFormat' given to va_start() is not last named argument of the function. Did you intend to pass 'nSize'? \n " , errout . str ( ) ) ;
check ( " void Format(char* szFormat, char* szBuffer, size_t nSize, ...) { \n "
" va_list arg_ptr; \n "
" va_start(arg_ptr, szBuffer); \n "
" va_end(arg_ptr); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (warning) 'szBuffer' given to va_start() is not last named argument of the function. Did you intend to pass 'nSize'? \n " , errout . str ( ) ) ;
check ( " void Format(char* szFormat, char* szBuffer, size_t nSize, ...) { \n "
" va_list arg_ptr; \n "
" va_start(arg_ptr, nSize); \n "
" va_end(arg_ptr); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2014-08-06 19:34:31 +02:00
check ( " int main(int argc, char *argv[]) { \n "
" long(^addthem)(const char *, ...) = ^long(const char *format, ...) { \n "
" va_list argp; \n "
" va_start(argp, format); \n "
" c = va_arg(argp, int); \n "
" }; \n "
" } " ) ; // Don't crash (#6032)
ASSERT_EQUALS ( " [test.cpp:6]: (error) va_list 'argp' was opened but not closed by va_end(). \n " , errout . str ( ) ) ;
2014-08-06 20:49:27 +02:00
check ( " void Format(char* szFormat, char* szBuffer, size_t nSize, ...) { \n "
" va_list arg_ptr; \n "
" va_start(arg_ptr); \n "
" va_end(arg_ptr); \n "
" } " ) ; // Don't crash if less than expected arguments are given.
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2015-01-27 21:21:03 +01:00
check ( " void assertf_fail(const char *assertion, const char *file, int line, const char *func, const char* msg, ...) { \n "
" struct A { \n "
" A(char* buf, int size) {} \n "
" void printf(const char * format, ...) { \n "
" va_list args; \n "
" va_start(args, format); \n "
" va_end(args); \n "
" } \n "
" }; \n "
" } " ) ; // Inner class (#6453)
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2014-08-06 14:20:46 +02:00
}
2014-11-20 14:20:09 +01:00
void referenceAs_va_start ( ) {
2014-08-06 14:20:46 +02:00
check ( " void Format(char* szFormat, char (&szBuffer)[_Size], ...) { \n "
" va_list arg_ptr; \n "
" va_start(arg_ptr, szBuffer); \n "
" va_end(arg_ptr); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (error) Using reference 'szBuffer' as parameter for va_start() results in undefined behaviour. \n " , errout . str ( ) ) ;
check ( " void Format(char* szFormat, char (*szBuffer)[_Size], ...) { \n "
" va_list arg_ptr; \n "
" va_start(arg_ptr, szBuffer); \n "
" va_end(arg_ptr); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
}
2014-11-20 14:20:09 +01:00
void va_end_missing ( ) {
2014-08-06 14:20:46 +02:00
check ( " void Format(char* szFormat, char (*szBuffer)[_Size], ...) { \n "
" va_list arg_ptr; \n "
" va_start(arg_ptr, szBuffer); \n "
" va_end(arg_ptr); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2014-09-27 11:03:58 +02:00
// #6186
check ( " void Format(char* szFormat, char (*szBuffer)[_Size], ...) { \n "
" va_list arg_ptr; \n "
" va_start(arg_ptr, szBuffer); \n "
" try { \n "
" throw sth; \n "
" } catch(...) { \n "
" va_end(arg_ptr); \n "
" } \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2014-08-06 14:20:46 +02:00
check ( " void Format(char* szFormat, char (*szBuffer)[_Size], ...) { \n "
" va_list arg_ptr; \n "
" va_start(arg_ptr, szBuffer); \n "
" } " ) ;
2014-08-06 14:26:48 +02:00
ASSERT_EQUALS ( " [test.cpp:4]: (error) va_list 'arg_ptr' was opened but not closed by va_end(). \n " , errout . str ( ) ) ;
2014-08-06 14:20:46 +02:00
check ( " void Format(char* szFormat, char (*szBuffer)[_Size], ...) { \n "
" va_list arg_ptr; \n "
" va_start(arg_ptr, szBuffer); \n "
" if(sth) return; \n "
" va_end(arg_ptr); \n "
" } " ) ;
2014-08-06 14:26:48 +02:00
ASSERT_EQUALS ( " [test.cpp:4]: (error) va_list 'arg_ptr' was opened but not closed by va_end(). \n " , errout . str ( ) ) ;
2019-01-10 18:32:17 +01:00
// #8124
check ( " void f(int n, ...) \n "
" { \n "
" va_list ap; \n "
" va_start(ap, n); \n "
" std::vector<std::string> v(n); \n "
" std::generate_n(v.begin(), n, [&ap]() \n "
" { \n "
" return va_arg(ap, const char*); \n "
" }); \n "
" va_end(ap); \n "
2021-02-20 12:58:42 +01:00
" } " ) ;
2019-01-10 18:32:17 +01:00
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void f(int n, ...) \n "
" { \n "
" va_list ap; \n "
" va_start(ap, n); \n "
" std::vector<std::string> v(n); \n "
" std::generate_n(v.begin(), n, [&ap]() \n "
" { \n "
" return va_arg(ap, const char*); \n "
" }); \n "
2021-02-20 12:58:42 +01:00
" } " ) ;
2019-01-10 18:32:17 +01:00
ASSERT_EQUALS ( " [test.cpp:10]: (error) va_list 'ap' was opened but not closed by va_end(). \n " , errout . str ( ) ) ;
2014-08-06 14:20:46 +02:00
}
2014-11-20 14:20:09 +01:00
void va_list_usedBeforeStarted ( ) {
2014-08-06 14:20:46 +02:00
check ( " void Format(char* szFormat, char (*szBuffer)[_Size], ...) { \n "
" va_list arg_ptr; \n "
" return va_arg(arg_ptr, float); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (error) va_list 'arg_ptr' used before va_start() was called. \n " , errout . str ( ) ) ;
check ( " void Format(char* szFormat, char (*szBuffer)[_Size], ...) { \n "
" va_list arg_ptr; \n "
" foo(arg_ptr); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (error) va_list 'arg_ptr' used before va_start() was called. \n " , errout . str ( ) ) ;
check ( " void Format(char* szFormat, char (*szBuffer)[_Size], ...) { \n "
" va_list arg_ptr; \n "
" va_copy(f, arg_ptr); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (error) va_list 'arg_ptr' used before va_start() was called. \n " , errout . str ( ) ) ;
check ( " void Format(char* szFormat, char (*szBuffer)[_Size], ...) { \n "
" va_list arg_ptr; \n "
" va_start(arg_ptr, szBuffer); \n "
" va_end(arg_ptr); \n "
" return va_arg(arg_ptr, float); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:5]: (error) va_list 'arg_ptr' used before va_start() was called. \n " , errout . str ( ) ) ;
2017-10-14 18:28:26 +02:00
check ( " void Format(char* szFormat, char (*szBuffer)[_Size], ...) { \n "
" va_list arg_ptr; \n "
" va_end(arg_ptr); \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:3]: (error) va_list 'arg_ptr' used before va_start() was called. \n " , errout . str ( ) ) ;
2014-08-06 14:20:46 +02:00
check ( " void Format(char* szFormat, char (*szBuffer)[_Size], ...) { \n "
" va_list arg_ptr; \n "
" va_start(arg_ptr, szBuffer); \n "
" va_end(arg_ptr); \n "
" va_start(arg_ptr, szBuffer); \n "
" foo(va_arg(arg_ptr, float)); \n "
" va_end(arg_ptr); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2014-08-18 10:40:39 +02:00
// #6066
check ( " void Format(va_list v1) { \n "
" va_list v2; \n "
" va_copy(v2, v1); \n "
" foo(va_arg(v1, float)); \n "
" va_end(v2); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2016-06-02 09:32:45 +02:00
// #7527
check ( " void foo(int flag1, int flag2, ...) { \n "
" switch (flag1) { \n "
" default: \n "
" va_list vargs; \n "
" va_start(vargs, flag2); \n "
" if (flag2) { \n "
" va_end(vargs); \n "
" break; \n "
" } \n "
" int data = va_arg(vargs, int); \n "
" va_end(vargs); \n "
" } \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2016-06-07 19:28:32 +02:00
// #7533
check ( " void action_push(int type, ...) { \n "
" va_list args; \n "
" va_start(args, type); \n "
" switch (push_mode) { \n "
" case UNDO: \n "
" list_add(&act->node, &to_redo); \n "
" break; \n "
" case REDO: \n "
" list_add(&act->node, &to_undo); \n "
" break; \n "
" } \n "
" va_end(args); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void action_push(int type, ...) { \n "
" va_list args; \n "
" va_start(args, type); \n "
" switch (push_mode) { \n "
" case UNDO: \n "
" list_add(&act->node, &to_redo); \n "
" va_end(args); \n "
" break; \n "
" case REDO: \n "
" list_add(&act->node, &to_undo); \n "
" va_end(args); \n "
" break; \n "
" } \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
check ( " void action_push(int type, ...) { \n "
" va_list args; \n "
" va_start(args, type); \n "
" switch (push_mode) { \n "
" case UNDO: \n "
" list_add(&act->node, &to_redo); \n "
" break; \n "
" case REDO: \n "
" list_add(&act->node, &to_undo); \n "
" va_end(args); \n "
" break; \n "
" } \n "
" } " ) ;
ASSERT_EQUALS ( " [test.cpp:13]: (error) va_list 'args' was opened but not closed by va_end(). \n " , errout . str ( ) ) ;
2017-05-07 08:32:48 +02:00
// #8043
check ( " void redisvFormatCommand(char *format, va_list ap, bool flag) { \n "
" va_list _cpy; \n "
" va_copy(_cpy, ap); \n "
" if (flag) \n "
" goto fmt_valid; \n "
" va_end(_cpy); \n "
" goto format_err; \n "
" fmt_valid: \n "
" sdscatvprintf(curarg, _format, _cpy); \n "
" va_end(_cpy); \n "
" format_err: \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2014-08-06 14:20:46 +02:00
}
2014-11-20 14:20:09 +01:00
void va_start_subsequentCalls ( ) {
2014-08-06 14:20:46 +02:00
check ( " void Format(char* szFormat, char (*szBuffer)[_Size], ...) { \n "
" va_list arg_ptr; \n "
" va_start(arg_ptr, szBuffer); \n "
" va_start(arg_ptr, szBuffer); \n "
" va_end(arg_ptr); \n "
" } " ) ;
2015-08-11 13:47:48 +02:00
ASSERT_EQUALS ( " [test.cpp:4]: (error) va_start() or va_copy() called subsequently on 'arg_ptr' without va_end() in between. \n " , errout . str ( ) ) ;
2014-08-06 14:20:46 +02:00
check ( " void Format(char* szFormat, char (*szBuffer)[_Size], ...) { \n "
" va_list vl1; \n "
" va_start(vl1, szBuffer); \n "
" va_copy(vl1, vl1); \n "
" va_end(vl1); \n "
" } " ) ;
2015-08-11 13:47:48 +02:00
ASSERT_EQUALS ( " [test.cpp:4]: (error) va_start() or va_copy() called subsequently on 'vl1' without va_end() in between. \n " , errout . str ( ) ) ;
2014-08-06 14:20:46 +02:00
check ( " void Format(char* szFormat, char (*szBuffer)[_Size], ...) { \n "
" va_list arg_ptr; \n "
" va_start(arg_ptr, szBuffer); \n "
" va_end(arg_ptr); \n "
" va_start(arg_ptr, szBuffer); \n "
" va_end(arg_ptr); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
}
2015-12-05 20:55:26 +01:00
void unknownFunctionScope ( ) {
check ( " void BG_TString::Format() { \n "
" BG_TChar * f; \n "
" va_start(args,f); \n "
" BG_TString result(f); \n "
" } " ) ;
ASSERT_EQUALS ( " " , errout . str ( ) ) ;
2016-06-20 09:24:23 +02:00
// #7559
check ( " void mowgli_object_message_broadcast(mowgli_object_t *self, const char *name, ...) { \n "
" va_list va; \n "
" MOWGLI_LIST_FOREACH(n, self->klass->message_handlers.head) { \n "
" if (!strcasecmp(sig2->name, name)) \n "
" break; \n "
" } \n "
" va_start(va, name); \n "
" va_end(va); \n "
" } " ) ;
2015-12-05 20:55:26 +01:00
}
2014-08-06 14:20:46 +02:00
} ;
REGISTER_TEST ( TestVaarg )