Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove undefined behavior and add stricter checks in console arg parsing #3613

Merged
merged 2 commits into from
Feb 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions common/include/pcl/console/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,16 @@ namespace pcl
PCL_EXPORTS int
parse_argument (int argc, const char * const * argv, const char * str, unsigned int &val);

/** \brief Parse for a specific given command line argument.
* \param[in] argc the number of command line arguments
* \param[in] argv the command line arguments
* \param[in] str the string value to search for
* \param[out] val the resultant value
* \return index of found argument or -1 if arguments do not appear in list
*/
PCL_EXPORTS int
parse_argument (int argc, const char * const * argv, const char * str, long int &val) noexcept;

/** \brief Parse for a specific given command line argument.
* \param[in] argc the number of command line arguments
* \param[in] argv the command line arguments
Expand Down
155 changes: 120 additions & 35 deletions common/src/parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
* Software License Agreement (BSD License)
*
* Point Cloud Library (PCL) - www.pointclouds.org
* Copyright (c) 2010-2012, Willow Garage, Inc.
* Copyright (c) 2012-, Open Perception, Inc.
* Copyright (c) 2020-, Open Perception, Inc.
*
* All rights reserved.
*
Expand Down Expand Up @@ -37,17 +36,16 @@
*/

#include <cctype>
#include <cerrno>
#include <complex>
#include <cstdio>
#include <limits>
#include <type_traits>

#include <pcl/console/parse.h>
#include <pcl/console/print.h>
#include <boost/algorithm/string.hpp>

////////////////////////////////////////////////////////////////////////////////
bool
pcl::console::find_switch (int argc, const char * const * argv, const char * argument_name)
{
return (find_argument (argc, argv, argument_name) != -1);
}
#include <boost/algorithm/string.hpp>

////////////////////////////////////////////////////////////////////////////////
int
Expand All @@ -64,6 +62,13 @@ pcl::console::find_argument (int argc, const char * const * argv, const char * a
return (-1);
}

////////////////////////////////////////////////////////////////////////////////
bool
pcl::console::find_switch (int argc, const char * const * argv, const char * argument_name)
{
return (find_argument (argc, argv, argument_name) != -1);
}

////////////////////////////////////////////////////////////////////////////////
int
pcl::console::parse_argument (int argc, const char * const * argv, const char * str, std::string &val)
Expand All @@ -76,63 +81,143 @@ pcl::console::parse_argument (int argc, const char * const * argv, const char *
}

////////////////////////////////////////////////////////////////////////////////
int
pcl::console::parse_argument (int argc, const char * const * argv, const char * str, bool &val)
namespace pcl
{
namespace console
{
template <class T, class V = T(*)(const char*, const char**)> int
parse_generic (V convert_func, int argc, const char* const* argv, const char* str, T& val)
{
char *endptr = nullptr;
int index = find_argument (argc, argv, str) + 1;
errno = 0;

if (index > 0 && index < argc )
val = atoi (argv[index]) == 1;
{
val = convert_func (argv[index], &endptr); // similar to strtol, strtod, strtof
// handle out-of-range, junk at the end and no conversion
if (errno == ERANGE || *endptr != '\0' || str == endptr)
{
return -1;
}
}

return (index - 1);
}

////////////////////////////////////////////////////////////////////////////////
int
pcl::console::parse_argument (int argc, const char * const * argv, const char * str, double &val)
parse_argument (int argc, const char * const * argv, const char * str, long int &val) noexcept
{
int index = find_argument (argc, argv, str) + 1;

if (index > 0 && index < argc )
val = atof (argv[index]);
const auto strtol_10 = [](const char *str, char **str_end){ return strtol(str, str_end, 10); };
return parse_generic(strtol_10, argc, argv, str, val);
}

return (index - 1);
int
parse_argument (int argc, const char * const * argv, const char * str, long long int &val) noexcept
{
const auto strtoll_10 = [](const char *str, char **str_end){ return strtoll(str, str_end, 10); };
return parse_generic(strtoll_10, argc, argv, str, val);
}

////////////////////////////////////////////////////////////////////////////////
int
pcl::console::parse_argument (int argc, const char * const * argv, const char * str, float &val)
parse_argument (int argc, const char * const * argv, const char * str, unsigned long long int &val) noexcept
{
int index = find_argument (argc, argv, str) + 1;
long long int dummy;
const auto ret = parse_argument (argc, argv, str, dummy);
if ((ret == -1) || dummy < 0)
{
return -1;
}
val = dummy;
return ret;
}

if (index > 0 && index < argc )
val = static_cast<float> (atof (argv[index]));
namespace detail
{
template <typename T, typename U>
constexpr auto legally_representable_v = (std::numeric_limits<T>::max () >= std::numeric_limits<U>::max ()) &&
(std::numeric_limits<T>::lowest () <= std::numeric_limits<U>::lowest ());
template <typename T, typename U>
struct legally_representable {
constexpr static bool value = legally_representable_v<T, U>;
};

// assumptions:
// * either long int or long long int is a valid type for storing Integral
// * unsigned long long int is handled specially
template <typename Integral>
using primary_legal_input_type = std::conditional_t<legally_representable_v<long int, Integral>,
long int, long long int>;

// special handling if unsigned [long] int is of same size as long long int
template <typename Integral>
using legal_input_type = std::conditional_t<(std::is_unsigned<Integral>::value &&
(sizeof (Integral) == sizeof (long long int))),
unsigned long long int,
primary_legal_input_type<Integral>>;
}

return (index - 1);
template <typename T>
using IsIntegral = std::enable_if_t<std::is_integral<T>::value, bool>;

template <typename T, IsIntegral<T> = true> int
parse_argument (int argc, const char * const * argv, const char * str, T &val) noexcept
{
using InputType = detail::legal_input_type<T>;
InputType dummy;
const auto ret = parse_argument (argc, argv, str, dummy);
if ((ret == -1) ||
(dummy < static_cast<InputType> (std::numeric_limits<T>::min ())) ||
(dummy > static_cast<InputType> (std::numeric_limits<T>::max ())))
{
return -1;
}

val = static_cast<T> (dummy);
return ret;
}
}
}

////////////////////////////////////////////////////////////////////////////////
int
pcl::console::parse_argument (int argc, const char * const * argv, const char * str, int &val)
pcl::console::parse_argument (int argc, const char * const * argv, const char * str, double &val)
{
int index = find_argument (argc, argv, str) + 1;

if (index > 0 && index < argc )
val = atoi (argv[index]);
return parse_generic(strtod, argc, argv, str, val);
}

return (index - 1);
////////////////////////////////////////////////////////////////////////////////
int
pcl::console::parse_argument (int argc, const char * const * argv, const char * str, float &val)
{
return parse_generic(strtof, argc, argv, str, val);
}

////////////////////////////////////////////////////////////////////////////////
int
pcl::console::parse_argument (int argc, const char * const * argv, const char * str, unsigned int &val)
{
int index = find_argument (argc, argv, str) + 1;
return parse_argument<unsigned int> (argc, argv, str, val);
}

if (index > 0 && index < argc )
val = atoi (argv[index]);
////////////////////////////////////////////////////////////////////////////////
int
pcl::console::parse_argument (int argc, const char * const * argv, const char * str, int &val)
{
return parse_argument<int> (argc, argv, str, val);
}

return (index - 1);
////////////////////////////////////////////////////////////////////////////////
int
pcl::console::parse_argument (int argc, const char * const * argv, const char * str, bool &val)
{
long int dummy;
const auto ret = parse_argument (argc, argv, str, dummy);
if (ret != -1)
{
val = static_cast<bool> (dummy);
}
return ret;
}

////////////////////////////////////////////////////////////////////////////////
Expand Down
1 change: 1 addition & 0 deletions test/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ PCL_ADD_TEST(common_test_wrappers test_wrappers FILES test_wrappers.cpp LINK_WIT
PCL_ADD_TEST(common_test_macros test_macros FILES test_macros.cpp LINK_WITH pcl_gtest pcl_common)
PCL_ADD_TEST(common_vector_average test_vector_average FILES test_vector_average.cpp LINK_WITH pcl_gtest)
PCL_ADD_TEST(common_common test_common FILES test_common.cpp LINK_WITH pcl_gtest pcl_common)
PCL_ADD_TEST(common_parse test_parse FILES test_parse.cpp LINK_WITH pcl_gtest pcl_common)
PCL_ADD_TEST(common_geometry test_geometry FILES test_geometry.cpp LINK_WITH pcl_gtest pcl_common)
PCL_ADD_TEST(common_copy_point test_copy_point FILES test_copy_point.cpp LINK_WITH pcl_gtest pcl_common)
PCL_ADD_TEST(common_transforms test_transforms FILES test_transforms.cpp LINK_WITH pcl_gtest pcl_common)
Expand Down
Loading