Search code examples
c++boostklocwork

boost::program_options generating MLK.MUST in Klocwork


I wrote the following C++ code based on the Boost library to take inputs from command line.

#include <iostream>
#include <boost/program_options.hpp>
#include <boost/format.hpp>

using namespace std;
namespace po = boost::program_options;

int main(int argc, char** argv) {
    int party = 2, port = 9999;
    string server_ip;
    
    po::options_description desc{"Allowed options"};
    desc.add_options()  //
    ("help,h", "produce help message")  //
    ("party,k", po::value<int>(&party)->default_value(1), "party id: 1 for server, 2 for client")  //
    ("port,p", po::value<int>(&port)->default_value(1234), "socket port")  //
    ("server_ip,s", po::value<string>(&server_ip)->default_value("localhost"), "server's IP.");
    
    po::variables_map vm;
    try {
        po::parsed_options parsed = po::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
        po::store(parsed, vm);
        if (vm.count("help")) {
            cout << desc << endl;
            return 0;
        }
        po::notify(vm);
    }catch (po::error& e) {
        cout << "ERROR: " << e.what() << endl << endl;
        cout << desc << endl;
        return -1;
    }
    
    cout << party << endl;
    cout << port << endl;
    cout << server_ip << endl;
}

It works as intended. However, Klocwork reported the following error (I have adjusted the line numbers for this code snippet):

main.cpp:16 MLK.MUST (2:Error) Analyze
Memory leak. Dynamic memory stored in 'po::value<int> ( &party)' allocated through function 'value<int>' at line 14 is lost at line 16
  * main.cpp:14: Dynamic memory stored in 'po::value<int> ( &party)' is allocated by calling function 'value<int>'.
    * value_semantic.hpp:198: 'r' is allocated by function 'new'.
  * main.cpp:16: Dynamic memory stored in 'po::value<int> ( &party)' is lost.
Current status 'Analyze'

I found this old post boost program_options generating a Klocwork MLK.MUST. However, after reading the answer, I still do not know how I can solve this issue.

Another issue reported by Klocwork is 'port' is used uninitialized in this function. It specifically mentions that passing '&port' to 'po::value<int>' does not initialize 'port'. However, after running the code, I see that it does initialize port since the value of port is printed as 1234 and not 9999.

Is there a way to write this code that will solve the above issues?


Solution

  • Another issue reported by Klocwork is 'port' is used uninitialized in this function. It specifically mentions that passing '&port' to 'po::value'

    This is a false positive: nothing uses the value of port before it's initialized (I checked). However, it should be enough to actually initializing port to silence the message, by. It's weird that it still triggers, since you already had that.

    Neither vaglrind nor ASAN+UBSAN find anything wrong with the code for me. Here's a brute force test that tries all kinds of option combinations (including unregistered and erroneous):

    #!/bin/bash
    set -e -u
    opts=( '' '-k two' '-k 2' '-p 2345' '-s 127.0.0.88' 'bogus' '--more-bogus');
    for a in "${opts[@]}"
    do
        for b in "${opts[@]}"
        do
            for c in "${opts[@]}"
            do
                valgrind ./sotest "$a" "$b" "$c"
            done
        done
    done
    

    That ends up running 343 different invocations of the program and ends up printing the expected outputs:

     69x 2
     69x 2345
     69x 127.0.0.88
    
     99x 1
     99x 1234
     99x localhost
    

    The expected diagnostics:

     17x option '--port' cannot be specified more than once
     17x option '--server_ip' cannot be specified more than once
     34x option '--party' cannot be specified more than once
    107x the argument ('two') for option '--party' is invalid
    

    And most importantly, consistent leak-free report:

    343 All heap blocks were freed -- no leaks are possible
    

    TL;DR

    I don't know why your tooling reports leaks. At the very least, the "'port' is used uninitialized" issue seems wrong on close inspection.

    I tested on GCC 10 with Boost 1.73.0, -std=c++17 -O3, your source 1--% unaltered.

    I hope this gives you more ideas and perhaps some reassurance.