[C language learning notes]: Security

Modifying constvariables or methods to tell the compiler that these are immutable helps the compiler optimize the code and helps developers understand whether the function has side effects. Additionally, using const &prevents the compiler from copying unnecessary data. John Carmack's comment on ```const```[2] is worth reading.

// Bad Idea
class MyClass
{
public:
  void do_something(int i);
  void do_something(std::string str);
};


// Good Idea
class MyClass
{
public:
  void do_something(const int i);
  void do_something(const std::string &str);
};

Think carefully about return types

  • Getters (member variable reading API)

    • Under normal circumstances, when reading member variables through return values, using &or const &return values ​​can significantly improve performance.

    • Returning by value is more conducive to thread safety. If the returned value is for copying and use, there will be no performance loss.

    • If the API return value uses covariant return types, it must return &or*

  • Temporary and local values

    • Always returned by value

Don't use const references to pass and return simple types

// Very Bad Idea
class MyClass
{
public:
  explicit MyClass(const int& t_int_value)
    : m_int_value(t_int_value)
  {
  }

  const int& get_int_value() const
  {
    return m_int_value;
  }

private:
  int m_int_value;
}

Instead, simple types are passed and returned by value. If you don't plan to change the passed values, declare them as const, but not as constreferences:

// Good Idea
class MyClass
{
public:
  explicit MyClass(const int t_int_value)
    : m_int_value(t_int_value)
  {
  }

  int get_int_value() const
  {
    return m_int_value;
  }

private:
  int m_int_value;
}

Why is this so? Because passing and returning by reference results in pointer operations, passing by value is handled in processor registers and is faster.

Avoid accessing raw memory

It is difficult in C++ to correctly handle raw memory access, allocation, and deallocation without the risk of memory errors and leaks [3], and C++11 provides tools to avoid these problems.

// Bad Idea
MyClass *myobj = new MyClass;

// ...
delete myobj;


// Good Idea
auto myobj = std::make_unique<MyClass>(constructor_param1, constructor_param2); // C++14
auto myobj = std::unique_ptr<MyClass>(new MyClass(constructor_param1, constructor_param2)); // C++11
auto mybuffer = std::make_unique<char[]>(length); // C++14
auto mybuffer = std::unique_ptr<char[]>(new char[length]); // C++11

// or for reference counted objects
auto myobj = std::make_shared<MyClass>(); 

// ...
// myobj is automatically freed for you whenever it is no longer used.

Use std::arrayor std::vectorinstead of C-style arrays

Both methods guarantee a contiguous memory layout of objects and can (and should) completely replace C-style arrays, and are one of the many reasons not to use raw pointers.

Also, avoid using ```std::shared_ptr``` to save arrays[4].

Use exception

Return values ​​(for example boost::optional), can be ignored and may cause crashes or memory errors if not checked, whereas exceptions cannot be ignored. Exceptions, on the other hand, can be caught and handled. It is possible that the exception will rise to the highest level of the application, where it will be caught, recorded in the log, and trigger an automatic restart of the application.

Stroustrup, one of the designers of C++, talked about this topic: Why use exceptions?[5]

Use C++-style casts instead of C-style casts

Replace C-style casts with C++-style casts ( static_cast<>, dynamic_cast<>, ...), which allow more compiler checks and are quite safe.

// Bad Idea
double x = getX();
int i = (int) x;

// Not a Bad Idea
int i = static_cast<int>(x);

In addition, the C++ type conversion style is more explicit and more search-friendly.

But if you need to doubleconvert types to inttypes, consider refactoring your program logic (e.g. additional checks for overflow and underflow). Avoid the situation of measuring 3 times and then cutting 0.9999999999981 times.

Do not define variadic functions

A variadic function can accept a variable number of arguments, perhaps the most famous example being printf(). Although it is possible to define such functions, there may be security risks. The use of variadic functions is not type-safe, and incorrect input parameters may cause the program to terminate with undefined behavior. This undefined behavior may cause security issues. If you use a compiler that supports C++1, you can use variadic templates.

Guess you like

Origin blog.csdn.net/Jiangziyadizi/article/details/129372662