• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# C&C++ Secure Coding Guide
2
3This document provides some secure coding suggestions based on the C\&C++ language to guide development.
4
5# Functions
6
7## Check the validity of all values received from external sources
8
9**\[Description]**
10
11External sources are networks, user input, command lines, files (including program configuration files), environment variables, user-mode data (for kernel programs), inter-process communications (including pipes, messages, shared memory, sockets, RPCs, and communications between different boards in a device), API parameters, and global variables.
12
13Data from outside programs is often considered untrusted and needs to be properly checked for validity before being used. If data from an external source is not checked before use, unexpected security risks may occur.
14
15Note: Do not use assertions to check external input data. Assertions should be used to prevent incorrect program assumptions but cannot be used to check for runtime errors in a released version.
16
17Data from outside programs must be checked before being used. Typical scenarios include:
18
19- **Used as an array index**
20
21  If untrusted data is used as an array index, the array upper bound may be exceeded, causing invalid memory access.
22
23- **Used as a memory offset address**
24
25  Using untrusted data as the pointer offset for memory access may result in invalid memory access and cause further damages, for example, any address read/write.
26
27- **Used as a memory allocation size parameter**
28
29  Zero-byte allocation may cause invalid memory access; an unrestricted memory allocation size leads to excessive resource consumption.
30
31- **Used a loop condition**
32
33  If untrusted data is used as a loop condition, problems such as buffer overflow, out-of-bounds read/write, and infinite loop may occur.
34
35- **Used as a divisor**
36
37  Divide-by-zero errors may occur.
38
39- **Used as a command line parameter**
40
41  Command injection vulnerabilities may occur.
42
43- **Used as the parameter of a database query statement**
44
45  SQL injection vulnerabilities may occur.
46
47- **Used as an input/output format string**
48
49  Format string vulnerabilities may occur.
50
51- **Used as a memory copy length**
52
53  Buffer overflows may occur.
54
55- **Used a file path**
56
57  Direct access to an untrusted file path may result in directory traversal attacks. As a result, the system is controlled by the attacker who can perform file operations without permissions.
58
59Input validation includes but is not limited to:
60
61- API parameter validity check
62- Data length check
63- Data range check
64- Data type and format check
65- Check on inputs that can only contain permitted characters (in the trustlist), especially special characters in certain cases.
66
67**External Data Validation Principles**
68
691. Trust boundary
70
71   External data is untrusted. Therefore, if data is transmitted and processed across different trust boundaries during system operation, validity check must be performed on data from modules outside the trust boundaries to prevent attacks from spreading.
72
73   (a) Different so (or dll) modules
74
75   As an independent third-party module, the so or dll module is used to export common API functions for other modules to call. The so/dll module is unable to determine whether the caller passes on valid arguments. Therefore, the common function of the so/dll module needs to check the validity of the arguments provided by the caller. The so/dll module should be designed in low coupling and high reusability. Although the so/dll module is designed to be used only in this software in certain cases, different so/dll modules should still be regarded as different trust boundaries.
76
77   (b) Different processes
78
79   To prevent privilege escalation through processes with high permissions, the IPC communications between processes (including IPC communications between boards and network communications between hosts) should be regarded as communications across different trust boundaries.
80
81   (c) Application layer processes and operating system kernel
82   The operating system kernel has higher permissions than the application layer. The interface provided by the kernel for the application layer should process the data from the application layer as untrusted data.
83
84   (d) Internal and external environments of TEE
85   To prevent attacks from spreading to the TEE, the interfaces provided by the TEE and SGX for external systems should process external data as untrusted data.
86
872. External data validation
88
89   The external data received by a module must be validated before being used. After data validation is completed, the data stored in the module does not need to be verified again by other internal subfunctions in the module.
90
91**\[Noncompliant Code Example]**
92
93The **Foo()** function processes external data. Because the buffer does not necessarily end with '\\0', the **nameLen** value returned by **strlen** may exceed **len**. As a result, out-of-bounds read occurs.
94
95```cpp
96void Foo(const unsigned char* buffer, size_t len)
97{
98    // "buffer" may be a null pointer and may not end with '\0'.
99    const char* s = reinterpret_cast<const char*>(buffer);
100    size_t nameLen = strlen(s);
101    std::string name(s, nameLen);
102    Foo2(name);
103    ...
104}
105```
106
107**\[Compliant Code Example]**
108
109External data is checked for validity. In this example, **strnlen** is used to calculate the string length to reduce the risk of out-of-bounds read.
110
111```cpp
112void Foo(const unsigned char* buffer, size_t len)
113{
114    // Parameter validity check must be performed.
115    if (buffer == nullptr || len == 0 || len >= MAX_BUFFER_LEN) {
116        ... // Error handling
117    }
118
119    const char* s = reinterpret_cast<const char*>(buffer);
120    size_t nameLen = strnlen(s, len); // strnlen is used to mitigate the risk of out-of-bounds read.
121    if (nameLen == len) {
122        ... // Error handling
123    }
124    std::string name(s, nameLen);
125    ...
126    Foo2(name);
127    ...
128}
129```
130
131```cpp
132namespace ModuleA {
133// Foo2() is an internal function of the module. Parameter validity is ensured by the caller as agreed.
134static void Foo2(const std::string& name)
135{
136    ...
137    Bar(name.c_str()); // Call the function in MODULE_B.
138}
139
140// Foo() is an external interface of the module. Parameter validity check must be performed.
141void Foo(const unsigned char* buffer, size_t len)
142{
143    // Check the null pointer and valid parameter range.
144    if (buffer == nullptr || len <= sizeof(int)) {
145        // Error handling
146        ...
147    }
148
149    int nameLen = *(reinterpret_cast<const int*>(buffer)); // Obtain the length of the name character string from the packet.
150    // nameLen is untrusted data and its validity must be checked.
151    if (nameLen <= 0 || static_cast<size_t>(nameLen) > len - sizeof(int)) {
152        // Error handling
153        ...
154    }
155
156    std::string name(reinterpret_cast<const char*>(buffer), nameLen);
157    Foo2(name); // Call the internal functions of the module.
158    ...
159}
160}
161```
162
163The following code is the code in `MODULE_B` written using the C language:
164
165```cpp
166// Bar is a common function of MODULE_B.
167// If name is not nullptr, the string must be a valid string, which is longer than 0 bytes and null terminated.
168void Bar(const char* name)
169{
170    // Parameter validity check must be performed.
171    if (name == nullptr || name[0] == '\0') {
172        // Error handling
173        ...
174    }
175    size_t nameLen = strlen(name);  // strnlen does not need to be used.
176    ...
177}
178```
179
180For module A, the buffer is an external untrusted input, which must be strictly verified. Validity check is performed while the name is parsed from the buffer. The name is valid in module A, and validity check is not required when the name is transferred to internal subfunctions as a parameter. (If the name content needs to be parsed, it must be verified.) If the name in module A needs to be transferred to other modules across the trusted plane (in this example, the common function of module B is directly called, or by means of file, pipe, or network transfer), the name is untrusted data for module B and therefore validity check must be performed.
181
182# Classes
183
184## Class member variables must be explicitly initialized
185
186**\[Description]**
187
188If a class member variable is not explicitly initialized, the object will have an indeterminate value. If the class member variable has a default constructor, it does not need to be explicitly initialized.
189
190**\[Noncompliant Code Example]**
191
192```cpp
193class Message {
194public:
195    void Process()
196    {
197        ...
198    }
199
200private:
201    uint32_t msgId;                    // Noncompliant: The member variable is not initialized.
202    size_t msgLength;                  // Noncompliant: The member variable is not initialized.
203    unsigned char* msgBuffer;          // Noncompliant: The member variable is not initialized.
204    std::string someIdentifier;        // Only this member variable is initialized by the default constructor.
205};
206
207Message message;                       // The message member variable is not completely initialized.
208message.Process();                     // Potential risks exist in subsequent use.
209```
210
211**\[Compliant Code Example]**
212
213One practice is to explicitly initialize the class member variable in declarations.
214
215```cpp
216class Message {
217public:
218    void Process()
219    {
220        ...
221    }
222
223private:
224    uint32_t msgId{0};
225    size_t msgLength{0};
226    unsigned char* msgBuffer{nullptr};
227    std::string someIdentifier;        // The default constructor is used, and explicit initialization is not required.
228};
229```
230
231Another option is to initialize the list using a constructor.
232
233```cpp
234class Message {
235public:
236    Message() : msgId(0), msgLength(0), msgBuffer(nullptr) {}
237    void Process()
238    {
239        ...
240    }
241
242private:
243    uint32_t msgId;
244    size_t msgLength;
245    unsigned char* msgBuffer;
246    std::string someIdentifier;        // The default constructor is used, and explicit initialization is not required.
247};
248```
249
250## Clearly define the special member functions to be implemented
251
252**\[Description]**
253
254**Rule of three**
255
256If a class requires a user-defined destructor, a user-defined copy constructor, or a user-defined copy assignment operator, it almost certainly requires all three.
257
258```cpp
259class Foo {
260public:
261    Foo(const char* buffer, size_t size) { Init(buffer, size); }
262    Foo(const Foo& other) { Init(other.buf, other.size); }
263
264    Foo& operator=(const Foo& other)
265    {
266        Foo tmp(other);
267        Swap(tmp);
268        return *this;
269    }
270
271    ~Foo() { delete[] buf; }
272
273    void Swap(Foo& other) noexcept
274    {
275        using std::swap;
276        swap(buf, other.buf);
277        swap(size, other.size);
278    }
279
280private:
281    void Init(const char* buffer, size_t size)
282    {
283        this->buf = new char[size];
284        memcpy(this->buf, buffer, size);
285        this->size = size;
286    }
287
288    char* buf;
289    size_t size;
290};
291```
292
293The implicitly-defined special member functions are typically incorrect if the class manages a resource whose handle is an object of non-class type (such as the raw pointer or POSIX file descriptor), whose destructor does nothing and copy constructor/assignment operator performs a "shallow copy".
294
295Classes that manage non-copyable resources through copyable handles may have to declare copy assignment and copy constructor private and not provide their definitions or define them as deleted.
296
297**Rule of five**
298
299The presence of a user-defined destructor, copy-constructor, or copy-assignment operator can prevent implicit definition of the move constructor and the move assignment operator. Therefore, any class for which move semantics are desirable has to declare all five special member functions.
300
301```cpp
302class Foo {
303public:
304    Foo(const char* buffer, size_t size) { Init(buffer, size); }
305    Foo(const Foo& other) { Init(other.buf, other.size); }
306
307    Foo& operator=(const Foo& other)
308    {
309        Foo tmp(other);
310        Swap(tmp);
311        return *this;
312    }
313
314    Foo(Foo&& other) noexcept : buf(std::move(other.buf)), size(std::move(other.size))
315    {
316        other.buf = nullptr;
317        other.size = 0;
318    }
319
320    Foo& operator=(Foo&& other) noexcept
321    {
322        Foo tmp(std::move(other));
323        Swap(tmp);
324        return *this;
325    }
326
327    ~Foo() { delete[] buf; }
328
329    void Swap(Foo& other) noexcept
330    {
331        using std::swap;
332        swap(buf, other.buf);
333        swap(size, other.size);
334    }
335
336private:
337    void Init(const char* buffer, size_t size)
338    {
339        this->buf = new char[size];
340        memcpy(this->buf, buffer, size);
341        this->size = size;
342    }
343
344    char* buf;
345    size_t size;
346};
347```
348
349However, failure to provide the move constructor and move assignment operator is usually not an error, but a missed optimization opportunity.
350
351**Rule of zero**
352
353If a class does not need to deal exclusively with resource ownership, the class should not have custom destructors, copy/move constructors, or copy/move assignment operators.
354
355```cpp
356class Foo {
357public:
358    Foo(const std::string& text) : text(text) {}
359
360private:
361    std::string text;
362};
363```
364
365As long as a copy constructor, copy assignment operator, or destructor is declared for a class, the compiler will not implicitly generate move constructors or move assignment operators. As a result, the move operation of this class becomes a copy operation at a higher cost. As long as a move constructor or move assignment operator is declared for a class, the compiler will define the implicitly generated copy constructor or copy assignment operator as deleted. As a result, the class can only be moved but cannot be copied. Therefore, if any of the functions is declared, all the other functions should be declared to avoid unexpected results.
366
367Likewise, if a base class needs to define the virtual destructor as public, all related special member functions need to be implicitly defined:
368
369```cpp
370class Base {
371public:
372    ...
373    Base(const Base&) = default;
374    Base& operator=(const Base&) = default;
375    Base(Base&&) = default;
376    Base& operator=(Base&&) = default;
377    virtual ~Base() = default;
378    ...
379};
380```
381
382However, if a copy constructor/copy assignment operator is declared for a base class, slicing may occur. Therefore, the copy constructor/copy assignment operator in the base class is often explicitly defined as deleted, and other special member functions are also explicitly defined as deleted:
383
384```cpp
385class Base {
386public:
387    ...
388    Base(const Base&) = delete;
389    Base& operator=(const Base&) = delete;
390    Base(Base&&) = delete;
391    Base& operator=(Base&&) = delete;
392    virtual ~Base() = default;
393    ...
394};
395```
396
397## The copy constructor, copy assignment operator, move constructor, and move assignment operator in the base class must be defined as non-public or deleted
398
399**\[Description]**
400
401Slicing occurs if a derived class object is directly assigned to a base class object. In this case, only the base class part is copied or moved, which undermines polymorphism.
402
403**\[Noncompliant Code Example]**
404
405In the following code, the copy constructor and copy assignment operator of the base class are declared as default. Slicing occurs if a derived class object is assigned to the base class object. The copy constructor and copy assignment operator can be declared as deleted so that the compiler can check such assignment behavior.
406
407```cpp
408class Base {
409public:
410    Base() = default;
411    Base(const Base&) = default;
412    Base& operator=(const Base&) = default;
413    ...
414    virtual void Fun() { std::cout << "Base" << std::endl; }
415};
416
417class Derived : public Base {
418    ...
419    void Fun() override { std::cout << "Derived" << std::endl; }
420};
421
422void Foo(const Base& base)
423{
424    Base other = base;    // Noncompliant: Slicing occurs.
425    other.Fun();          // The Fun() function of the base class is called.
426}
427Derived d;
428Foo(d);
429```
430
431## The resources of the source object must be correctly reset in move constructors and move assignment operators
432
433**\[Description]**
434
435The move constructor and move assignment operator move the ownership of a resource from one object to another. Once the resource is moved, the resource of the source object should be reset correctly. This can prevent the source object from freeing the moved resources in destructors.
436
437Some non-resource data can be retained in the moved object, but the moved object must be in a state that can be properly destructed. Therefore, after an object is moved, do not reply on the value of the moved object unless the object is explicitly specified. lvalue reference may lead to unexpected behavior.
438
439**\[Noncompliant Code Example]**
440
441```cpp
442class Foo {
443public:
444    ...
445    Foo(Foo&& foo) noexcept : data(foo.data)
446    {
447    }
448
449    Foo& operator=(Foo&& foo)
450    {
451        data = foo.data;
452        return *this;
453    }
454
455    ~Foo()
456    {
457        delete[] data;
458    }
459
460private:
461    char* data = nullptr;
462};
463```
464
465The move constructor and move assignment operator of the **Foo()** function do not correctly reset the resources of the source object. When the source object is destructed, the resources will be released. As a result, the resources taken over by the newly created object become invalid.
466
467**\[Compliant Code Example]**
468
469```cpp
470class Foo {
471public:
472    ...
473    Foo(Foo&& foo) noexcept : data(foo.data)
474    {
475        foo.data = nullptr;
476    }
477
478    Foo& operator=(Foo&& foo)
479    {
480        if (this == &foo) {
481            return *this;
482        }
483        delete[] data;
484        data = foo.data;
485        foo.data = nullptr;
486        return *this;
487    }
488
489    ~Foo()
490    {
491        delete[] data;
492    }
493
494private:
495    char* data = nullptr;
496};
497```
498
499 In some standard libraries, the implementation of std::string may implement the short string optimization (SSO). The content of the character string to be moved may not be altered during the implementation of move semantics. As a result, the output of the following code may not be the expected b but ab, causing compatibility issues.
500
501```cpp
502std::string str{"a"};
503std::string other = std::move(str);
504
505str.append(1, 'b');
506std::cout << str << std::endl;
507```
508
509## The base class destructor must be declared as virtual when a derived class is released through a base class pointer
510
511**\[Description]**
512
513The destructor of the derived class can be called through polymorphism only when the base class destructor is declared as virtual. If the base class destructor is not declared as virtual, only the base class destructor (instead of the derived class destructor) is called when the derived class is released through a base class pointer, causing memory leaks.
514
515**\[Noncompliant Code Example]**
516
517Memory leaks occur because the base class destructor is not declared as virtual.
518
519```cpp
520class Base {
521public:
522    Base() = default;
523    ~Base() { std::cout << "~Base" << std::endl; }
524    virtual std::string GetVersion() = 0;
525};
526class Derived : public Base {
527public:
528    Derived()
529    {
530        const size_t numberCount = 100;
531        numbers = new int[numberCount];
532    }
533
534    ~Derived()
535    {
536        delete[] numbers;
537        std::cout << "~Derived" << std::endl;
538    }
539
540    std::string GetVersion()
541    {
542        return std::string("hello!");
543    }
544
545private:
546    int* numbers;
547};
548void Foo()
549{
550    Base* base = new Derived();
551    delete base;                // The base class destructor is called, causing resource leaks.
552}
553```
554
555## Avoid slicing during object assignment and initialization
556
557**\[Description]**
558
559Slicing occurs when a derived class object is assigned to a base class object, damaging polymorphism.
560
561If the object needs to be sliced, it is recommended that an explicit operation be defined for slicing, thereby avoiding misunderstanding and improving maintainability.
562
563**\[Noncompliant Code Example]**
564
565```cpp
566class Base {
567     virtual void Fun();
568};
569
570class Derived : public Base {
571    ...
572};
573void Foo(const Base& base)
574{
575    Base other = base;        // Noncompliant: Slicing occurs.
576    other.Fun();              // The Fun() function of the base class is called.
577}
578Derived d;
579Base b{d};                    // Noncompliant: Only base is constructed.
580b = d;                        // Noncompliant: Assigned only to base.
581
582Foo(d);
583```
584
585# Expressions and Statements
586
587## Ensure that objects have been initialized before being used
588
589**\[Description]**
590
591Initialization is the process of setting the expected value for an object by means of explicit initialization, default constructor initialization, and value assignment. Reading an uninitialized value may result in undefined behaviour. Therefore, ensure that objects have been initialized before being used.
592
593**\[Noncompliant Code Example]**
594
595```cpp
596void Bar(int data);
597...
598void Foo()
599{
600    int data;
601    Bar(data); // Noncompliant: Not initialized before being used
602    ...
603}
604```
605
606If there are different branches, ensure that all branches are initialized before being used as values.
607
608```cpp
609void Bar(int data);
610...
611void Foo(int condition)
612{
613    int data;
614    if (condition > 0) {
615        data = CUSTOMIZED_SIZE;
616    }
617    Bar(data);      // Noncompliant: Values not initialized for some branches
618    ...
619}
620```
621
622**\[Compliant Code Example]**
623
624```cpp
625void Bar(int data);
626...
627void Foo()
628{
629    int data{0};    // Compliant: Explicit initialization
630    Bar(data);
631    ...
632}
633void InitData(int& data);
634...
635void Foo()
636{
637    int data;
638    InitData(data); // Compliant: Initialization using functions
639    ...
640}
641std::string data;   // Compliant: Default constructor initialization
642...
643```
644
645## Avoid using reinterpret\_cast
646
647**\[Description]**
648
649`reinterpret_cast` is used to convert irrelevant types. `reinterpret_cast` tries to cast one type to another type, which destroys the type of security and reliability. It is an unsafe conversion. It is advised to use reinterpret\_cast as little as possible.
650
651## Avoid using const\_cast
652
653**\[Description]**
654
655`const_cast` is used to remove the `const` and `volatile` attributes of an object.
656
657Using a pointer or reference converted by **const\_cast** to modify a **const** or **volatile** object will result in undefined behavior.
658
659**\[Noncompliant Code Example]**
660
661```cpp
662const int i = 1024;
663int* p = const_cast<int*>(&i);
664*p = 2048;                              // Undefined behavior
665class Foo {
666public:
667    void SetValue(int v) { value = v; }
668
669private:
670    int value{0};
671};
672
673int main()
674{
675    const Foo foo;
676    Foo* p = const_cast<Foo*>(&foo);
677    p->SetValue(2);                     // Undefined behavior
678    return 0;
679}
680```
681
682## Ensure no overflows in signed integer operations
683
684**\[Description]**
685
686In the C++ standard, signed integer overflow is undefined behavior. Therefore, signed integer overflows are handled differently in implementations. For example, after defining a signed integer type as a modulus, the compiler may not detect integer overflows.
687
688Using overflowed values may cause out-of-bounds read/write risks in the buffer. For security purposes, ensure that operations do not cause overflows when signed integers in external data are used in the following scenarios:
689
690- Integer operand of pointer operation (pointer offset value)
691- Array index
692- Length of the variable-length array (and the length operation expression)
693- Memory copy length
694- Parameter of the memory allocation function
695- Loop judgment condition
696
697Integer promotion needs to be considered when the operation is performed for the integer types whose precision is less than **int**. Programmers also need to master integer conversion rules, including implicit conversion rules, to design secure arithmetic operations.
698
699**\[Noncompliant Code Example]**
700
701In the following code example, the integers involved in the subtraction operation are external data and are not validated before being used. As a result, integer overflow may occur, which further results in buffer overflow due to memory copy operations.
702
703```cpp
704unsigned char* content = ... // Pointer to the packet header
705size_t contentSize = ...     // Total length of the buffer
706int totalLen = ...           // Total length of the packet
707int skipLen = ...            // Data length that needs to be ignored from the parsed message
708
709std::vector<unsigned char> dest;
710
711// Using totalLen - skipLen to calculate the length of the remaining data is likely to cause integer overflows.
712std::copy_n(&content[skipLen], totalLen - skipLen, std::back_inserter(dest));
713...
714```
715
716**\[Compliant Code Example]**
717
718In the following code example, code is refactored to use the variable of the `size_t` type to indicate the data length and check whether the external data length is valid.
719
720```cpp
721unsigned char* content = ... // Pointer to the packet header
722size_t contentSize = ...     // Total length of the buffer
723size_t totalLen = ...        // Total length of the packet
724size_t skipLen = ...         // Data length that needs to be ignored from the parsed message
725
726if (skipLen >= totalLen || totalLen > contentSize) {
727    ... // Error handling
728}
729
730std::vector<unsigned char> dest;
731std::copy_n(&content[skipLen], totalLen - skipLen, std::back_inserter(dest));
732...
733```
734
735**\[Noncompliant Code Example]**
736
737In the following code example, the value range of external data is validated. However, the second type is `int`, and `std::numeric_limits<unsigned long>::max()` is incorrectly used as a validation condition. As a result, integer overflow occurs.
738
739```cpp
740int second = ... // External data
741
742 //The value range of unsigned long is incorrectly used for upper limit validation.
743if (second < 0 || second > (std::numeric_limits<unsigned long>::max() / 1000)) {
744    return -1;
745}
746int millisecond = second * 1000; // Integer overflow may occur.
747...
748```
749
750**\[Compliant Code Example]**
751
752One option is to change the second type to `unsigned long`. This solution is applicable to the scenario where the new variable type is more fit for service logic.
753
754```cpp
755unsigned long second = ... // Refactor the type to unsigned long.
756
757if (second > (std::numeric_limits<unsigned long>::max() / 1000)) {
758    return -1;
759}
760int millisecond = second * 1000;
761...
762```
763
764Another method is to change the upper limit to `std::numeric_limits<int>::max()`.
765
766```cpp
767int second = ... // External data
768
769if (second < 0 || second > (std::numeric_limits<int>::max() / 1000)) {
770    return -1;
771}
772int millisecond = second * 1000;
773```
774
775**\[Impact]**
776
777Integer overflows may cause buffer overflows and arbitrary code execution.
778
779## Ensure that unsigned integer operations do not wrap
780
781**\[Description]**
782
783Integer wrap may occur in the arithmetic operation results of unsigned integers, which may cause risks such as out-of-bounds read/write in the buffer. For security purposes, ensure that operations do not cause wrapping when unsigned integers in external data are used in the following scenarios:
784
785- Pointer offset value (integer operands in pointer arithmetic operations)
786- Array index value
787- Memory copy length
788- Parameter of the memory allocation function
789- Loop judgment condition
790
791**\[Noncompliant Code Example]**
792
793In the following code example, the program checks whether the total length of the next sub-packet and the processed packet exceeds the maximum packet length. The addition operation in the check condition may cause integer wrapping, causing potential validation bypassing issues.
794
795```cpp
796size_t totalLen = ...              // Total length of the packet
797size_t readLen = 0;                // Record the length of the processed packet.
798...
799size_t pktLen = ParsePktLen();     //  Length of the next sub-packet parsed from the network packet
800if (readLen + pktLen > totalLen) { // Integer wrapping may occur.
801  ... // Error handling
802}
803...
804readLen += pktLen;
805...
806```
807
808**\[Compliant Code Example]**
809
810The readLen variable is the length of the processed packet and is definitely less than totalLen. Therefore, the use of the subtraction operation instead of the addition operation will not bypass the condition check.
811
812```cpp
813size_t totalLen = ... // Total length of the packet
814size_t readLen = 0;   // Record the length of the processed packet.
815...
816size_t pktLen = ParsePktLen(); // From the network packet
817if (pktLen > totalLen - readLen) {
818  ... // Error handling
819}
820...
821readLen += pktLen;
822...
823```
824
825**\[Noncompliant Code Example]**
826
827In the following code example, integer wrapping may occur in the operation of len validation, resulting in condition check bypassing.
828
829```cpp
830size_t len =... // From the user-mode input
831
832if (SCTP_SIZE_MAX - len < sizeof(SctpAuthBytes)) { // Integer wrapping may occur in subtraction.
833    ... // Error handling
834}
835... = kmalloc(sizeof(SctpAuthBytes) + len, gfp);   // Integer wrapping may occur.
836...
837```
838
839**\[Compliant Code Example]**
840
841In the following code example, the subtraction operation is relocated (ensure that the value of the subtraction expression is not reversed during compilation) to avoid integer wrapping.
842
843```cpp
844size_t len =... // From the user-mode input
845
846if (len > SCTP_SIZE_MAX - sizeof(SctpAuthBytes)) { // Ensure no integer wrapping for the subtraction expression value during compilation.
847    ... // Error handling
848}
849... = kmalloc(sizeof(SctpAuthBytes) + len, gfp);
850...
851```
852
853**\[Exception]**
854
855Unsigned integers can exhibit modulo behavior (wrapping) when necessary for the proper execution of the program. It is recommended that the variable declaration and each operation on that integer be clearly commented as supporting modulo behavior.
856
857**\[Impact]**
858
859Integer wrapping is likely to cause buffer overflows and arbitrary code execution.
860
861## Ensure that division and remainder operations do not cause divide-by-zero errors
862
863**\[Description]**
864
865Division remainder operations performed on integers with the divisor of zero are undefined behavior. Ensure that the divisor is not 0 in division and remainder operations.
866
867The ISO/IEEE 754-1985 standard for binary floating-point arithmetic specifies the behavior and results of floating-point number division by zero. However, the presence of undefined behavior depends on whether the hardware and software environments comply with this standard. Therefore, before dividing a floating point number by zero, ensure that the hardware and software environments comply with the binary floating-point arithmetic. Otherwise, undefined behavior still exists.
868
869**\[Noncompliant Code Example]**
870
871```c
872size_t a = ReadSize();  // From external data
873size_t b = 1000 / a;    // Noncompliant: a may be 0
874size_t c = 1000 % a;    // Noncompliant: a may be 0
875...
876```
877
878**\[Compliant Code Example]**
879
880In the following code example, a=0 validation is added to prevent divide-by-zero errors.
881
882```c
883size_t a = ReadSize();  // From external data
884if (a == 0) {
885    ... // Error handling
886}
887size_t b = 1000 / a;    // Compliant: Ensure that a is not 0.
888size_t c = 1000 % a;    // Compliant: Ensure that a is not 0.
889...
890```
891
892**\[Impact]**
893
894Divide-by-zero errors are likely to cause DoS.
895
896## Bitwise operations can be performed only on unsigned integers
897
898**\[Description]**
899
900Undefined behavior may occur during bitwise operations on signed integers. To avoid undefined behavior, ensure that bitwise operations are performed only on unsigned integers. In addition, the unsigned integer type with less precision than **int** is promoted when a bitwise operation is performed on the unsigned integer. Then the bitwise operation is performed on the promoted integer. Therefore, beware of the bitwise operations on such unsigned integers to avoid unexpected results. The bitwise operators are as follows:
901
902- `~` (Complement operator)
903- `&` (AND)
904- `|` (OR)
905- `^` (XOR)
906- `>>` (Right shift operator)
907- `<<` (Left shift operator)
908- `&=`
909- `^=`
910- `|=`
911- `>>=`
912- `<<=`
913
914C++20 defines bitwise shift operations on signed integers, and such operations can be performed in compliance with C++20.
915
916**\[Noncompliant Code Example]**
917
918In versions earlier than C++20, the right shift operation `data >> 24` can be implemented as arithmetic (signed) shift or logic (unsigned) shift. If the value in `value << data` is a negative number or the result of the left shift operation is out of the representable range of the promoted integer type, undefined behavior occurs.
919
920```cpp
921int32_t data = ReadByte();
922int32_t value = data >> 24;   // The result of the right shift operation on a signed integer is implementation-defined.
923
924... // Check the valid data range.
925
926int32_t mask = value << data; // The left shift operation on a signed integer may cause undefined behavior.
927```
928
929**\[Compliant Code Example]**
930
931```cpp
932uint32_t data = static_cast<uint32_t>(ReadByte());
933uint32_t value = data >> 24;  // Bitwise operations are performed only on unsigned integers.
934
935... // Check the valid data range.
936
937uint32_t mask  = value << data;
938```
939
940If bitwise operations are performed on unsigned integers with less precision than `int`, the operation results may be unexpected due to integer promotions. In this case, you need to immediately convert the operation results to the expected types to avoid unexpected results.
941
942**\[Noncompliant Code Example]**
943
944```cpp
945uint8_t mask = 1;
946uint8_t value = (~mask) >> 4; // Noncompliant: The result of the ~ operation contains high-order data, which may not meet the expectation.
947```
948
949**\[Compliant Code Example]**
950
951```cpp
952uint8_t mask = 1;
953uint8_t value = (static_cast<uint8_t>(~mask)) >> 4; // Compliant: The result is converted to the expected type immediately after the ~ operation.
954```
955
956**\[Exception]**
957
958- A signed integer constant or enumerated value used as a bit flag can be used as an operand for the \& and \| operators.
959
960```cpp
961int fd = open(fileName, O_CREAT | O_EXCL, S_IRWXU | S_IRUSR);
962```
963
964- If a signed positive integer is known at compile time, it can be used as the right operand of a shift operator.
965
966```cpp
967constexpr int SHIFT_BITS = 3;
968...
969uint32_t id = ...;
970uint32_t type = id >> SHIFT_BITS;
971```
972
973# Resource Management
974
975## Ensure validation of external data that is used as an array index or memory operation length
976
977**\[Description]**
978
979When external data is used as an array index for memory access, the data size must be strictly validated to ensure that the array index is within the valid scope. Otherwise, serious errors may occur. Buffer overflows will occur if data is copied to the memory space insufficient for storing the data. To prevent such errors, limit the size of data to be copied based on the target capacity or ensure that the target capacity is sufficient to store the data to be copied.
980
981**\[Noncompliant Code Example]**
982
983In the following code example, the **SetDevId()** function has an off-by-one error. When index equals `DEV_NUM`, an element is written out of bounds.
984
985```cpp
986struct Dev {
987    int id;
988    char name[MAX_NAME_LEN];
989};
990
991static Dev devs[DEV_NUM];
992
993int SetDevId(size_t index, int id)
994{
995    if (index > DEV_NUM) {         // Off-by-one error
996        ... // Error handling
997    }
998
999    devs[index].id = id;
1000    return 0;
1001}
1002```
1003
1004**\[Compliant Code Example]**
1005
1006In the following code example, the index validation condition is modified to avoid the off-by-one error.
1007
1008```cpp
1009struct Dev {
1010    int id;
1011    char name[MAX_NAME_LEN];
1012};
1013
1014static Dev devs[DEV_NUM];
1015
1016int SetDevId(size_t index, int id)
1017{
1018    if (index >= DEV_NUM) {
1019        ... // Error handling
1020    }
1021    devs[index].id = id;
1022    return 0;
1023}
1024```
1025
1026**\[Noncompliant Code Example]**
1027
1028External input data may not be directly used as the memory copy length, but may be indirectly involved in memory copy operations. In the following code, **inputTable.count** is from external packets. It is used as the upper limit of the **for** loop body and indirectly involved in memory copy operations, instead of being directly used as the memory copy length. Buffer overflows may occur because the length is not validated.
1029
1030```cpp
1031struct ValueTable {
1032    size_t count;
1033    int val[MAX_NUMBERS];
1034};
1035
1036void ValueTableDup(const ValueTable& inputTable)
1037{
1038    ValueTable outputTable = {0, {0}};
1039    ...
1040    for (size_t i = 0; i < inputTable.count; i++) {
1041        outputTable.val[i] = inputTable.val[i];
1042    }
1043    ...
1044}
1045```
1046
1047**\[Compliant Code Example]**
1048
1049In the following code example, **inputTable.count** is validated.
1050
1051```cpp
1052struct ValueTable {
1053    size_t count;
1054    int val[MAX_NUMBERS];
1055};
1056
1057void ValueTableDup(const ValueTable& inputTable)
1058{
1059    ValueTable outputTable = {0, {0}};
1060    ...
1061    // Based on application scenarios, validate the cyclic length inputTable.count of external packets
1062    // and the array size outputTable.val to prevent buffer overflows.
1063    if (inputTable->count >
1064        sizeof(outputTable.val) / sizeof(outputTable.val[0])) {
1065        ... // Error handling
1066    }
1067    for (size_t i = 0; i < inputTable.count; i++) {
1068        outputTable.val[i] = inputTable.val[i];
1069    }
1070    ...
1071}
1072```
1073
1074**\[Impact]**
1075
1076If the length of the copied data is externally controllable, buffer overflows may occur during data copy operations, which may cause arbitrary code execution vulnerabilities.
1077
1078## Verify the requested memory size before requesting memory
1079
1080**\[Description]**
1081
1082When the requested memory size is an external input, it must be verified to prevent the request for zero-length memory or excessive and illegal memory requests. This is because memory resources are limited and can be exhausted. If the requested memory is too large (memory requested at a time is too large, or requested multiple times in a loop), resources may be used up unexpectedly. Unexpected buffer allocation may result from incorrect parameter values, improper range checks, integer overflows, or truncation. If memory requests are controlled by attackers, security issues such as buffer overflows may occur.
1083
1084**\[Noncompliant Code Example]**
1085
1086In the following code example, the memory space specified by **size** is dynamically allocated. However, **size** is not validated.
1087
1088```c
1089// size is not validated before being passed into to the DoSomething() function.
1090int DoSomething(size_t size)
1091{
1092    ...
1093    char* buffer = new char[size]; // size is not validated before being used in this function.
1094    ...
1095    delete[] buffer;
1096}
1097```
1098
1099**\[Compliant Code Example]**
1100
1101In the following code example, before the memory space specified by **size** is dynamically allocated, the validity check required by the program is performed.
1102
1103```c
1104// size is not validated before being passed into to the DoSomething() function.
1105int DoSomething(size_t size)
1106{
1107    // In this function, size is validated before being used. FOO_MAX_LEN is defined as the maximum memory space expected.
1108    if (size == 0 || size > FOO_MAX_LEN) {
1109        ... // Error handling
1110    }
1111    char* buffer = new char[size];
1112    ...
1113    delete[] buffer;
1114}
1115```
1116
1117**\[Impact]**
1118
1119If the size of the requested memory is externally controllable, resources may be exhausted, resulting in DoS.
1120
1121## An array should not be passed as a pointer separately when it is passed into a function as a parameter
1122
1123**\[Description]**
1124
1125When the function parameter type is array (not array reference) or pointer, the array that is being passed into a function is degraded to a pointer. As a result, the array length information is lost, causing potential out-of-bounds read/write issues. If a function receives only fixed-length arrays as parameters, define the parameter type as an array reference or `std::array`. If the function receives a pointer without a length, then the length should also be passed into the function as a parameter.
1126
1127**\[Noncompliant Code Example]**
1128
1129```cpp
1130constexpr int MAX_LEN = 1024;
1131constexpr int SIZE = 10;
1132
1133void UseArr(int arr[])
1134{
1135    for (int i = 0; i < MAX_LEN; i++) {
1136        std::cout << arr[i] << std::endl;
1137    }
1138}
1139
1140void Test()
1141{
1142    int arr[SIZE] = {0};
1143    UseArr(arr);
1144}
1145```
1146
1147**\[Compliant Code Example]**
1148
1149It is easier to use the combination of the pointer and length as a type. The following is a simple encapsulation example:
1150
1151```cpp
1152template <typename T>
1153class Slice {
1154public:
1155    template <size_t N>
1156    Slice(T (&arr)[N]) : data(arr), len(N) {}
1157
1158    template <size_t N>
1159    Slice(std::array<T, N> arr) : data(arr.data()), len(N) {}
1160
1161    Slice(T* arr, size_t n) : data(arr), len(n) {}
1162    ...
1163
1164private:
1165    T* data;
1166    size_t len;
1167};
1168
1169void UseArr(Slice<int> arr)
1170{
1171    for (int i = 0; i < arr.size(); i++) {
1172        std::cout << arr[i] << std::endl;
1173    }
1174}
1175
1176constexpr int SIZE = 10;
1177
1178void Test()
1179{
1180    int arr[SIZE] = {0};
1181    Slice<int> s{arr};
1182    UseArr(s);
1183}
1184```
1185
1186If project conditions permit, it is advised to use a mature library for parameter passing, such as the `std::span` type in C++20.
1187
1188If these utility classes are not used, you can pass the pointer and length as two parameters.
1189
1190```cpp
1191void UseArr(int arr[], size_t len)
1192{
1193    for (int i = 0; i < len; i++) {
1194        std::cout << arr[i] << std::endl;
1195    }
1196}
1197
1198constexpr int SIZE = 10;
1199
1200void Test()
1201{
1202    int arr[SIZE] = {0};
1203    UseArr(arr, sizeof(arr));
1204}
1205```
1206
1207## When a lambda escapes the current scope, do not capture local variables by reference
1208
1209**\[Description]**
1210
1211If a lambda is not limited to local use (for example, when it is transferred to the outside of a function or to another thread), do not capture local variables by reference. Capturing by reference in a lambda means storing a reference to a local object. If the life cycle of the lambda is longer than that of local variables, memory may be insecure.
1212
1213**\[Noncompliant Code Example]**
1214
1215```cpp
1216void Foo()
1217{
1218    int local = 0;
1219    // The local is captured by reference. The local no longer exists after the function is executed. Therefore, the Process() behavior is undefined.
1220    threadPool.QueueWork([&] { Process(local); });
1221}
1222```
1223
1224**\[Compliant Code Example]**
1225
1226```cpp
1227void Foo()
1228{
1229    int local = 0;
1230    // Capture the local by value. The local is always valid when Process() is called.
1231    threadPool.QueueWork([local] { Process(local); });
1232}
1233```
1234
1235## Assign a new value to the variable pointing to a resource handle or descriptor immediately after the resource is freed
1236
1237**\[Description]**
1238
1239Variables pointing to resource handles or descriptors include pointers, file descriptors, socket descriptors, and other variables pointing to resources. Take a pointer as an example. If a pointer that has successfully obtained a memory segment is not immediately set to **nullptr** after the memory segment is freed and no new object is allocated, the pointer is a dangling pointer. Operations on a dangling pointer may lead to double-free and access-freed-memory vulnerabilities. An effective way to mitigate these vulnerabilities is to immediately set freed pointers to new values, such as **nullptr**. For a global resource handle or descriptor, a new value must be set immediately after the resource is freed, so as to prevent the invalid value from being used. For a resource handle or descriptor that is used only in a single function, ensure that the invalid value is not used again after the resource is freed.
1240
1241**\[Noncompliant Code Example]**
1242
1243In the following code example, the message is processed based on the message type. After the message is processed, the memory to which the **body** points is freed, but the pointer is not set to **nullptr**. If other functions process the message structure again, double-free and access-freed-memory errors may occur.
1244
1245```c
1246int Fun()
1247{
1248    SomeStruct *msg = nullptr;
1249
1250    ... // Use new to allocate the memory space for msg and msg->body and initialize msg.
1251
1252    if (msg->type == MESSAGE_A) {
1253        ...
1254        delete msg->body;         // Noncompliant: The pointer is not set to bnullptrb after memory is freed.
1255    }
1256
1257    ...
1258
1259    // msg is saved to the global queue, and the freed body member may be used.
1260    if (!InsertMsgToQueue(msg)) {
1261        delete msg->body;         // The memory to which the body points may be freed again.
1262        delete msg;
1263        return -1;
1264    }
1265    return 0;
1266}
1267```
1268
1269**\[Compliant Code Example]**
1270
1271In the following code example, the released pointer is immediately set to **nullptr** to avoid double-free errors.
1272
1273```c
1274int Fun()
1275{
1276    SomeStruct *msg = nullptr;
1277
1278    ... // Use new to allocate the memory space for msg and msg->body and initialize msg.
1279
1280    if (msg->type == MESSAGE_A) {
1281        ...
1282        delete msg->body;
1283        msg->body = nullptr;
1284    }
1285
1286    ...
1287
1288    // msg saved to the global queue
1289    if (!InsertMsgToQueue(msg)) {
1290        delete msg->body;         // No need to assign nullptr because the pointer leaves the scope soon
1291        delete msg;               // No need to assign nullptr because the pointer leaves the scope soon
1292        return -1;
1293    }
1294    return 0;
1295}
1296```
1297
1298The default memory freeing function does not perform any action on NULL pointers.
1299
1300**\[Noncompliant Code Example]**
1301
1302In the following code example, no new value is assigned to the file descriptor after it is closed.
1303
1304```c
1305SOCKET s = INVALID_SOCKET;
1306int fd = -1;
1307...
1308closesocket(s);
1309...
1310close(fd);
1311...
1312```
1313
1314**\[Compliant Code Example]**
1315
1316In the following code example, a new value is assigned to the corresponding variable immediately after the resource is freed.
1317
1318```c
1319SOCKET s = INVALID_SOCKET;
1320int fd = -1;
1321...
1322closesocket(s);
1323s = INVALID_SOCKET;
1324...
1325close(fd);
1326fd = -1;
1327...
1328```
1329
1330**\[Impact]**
1331
1332The practices of using the freed memory, freeing the freed memory again, or using the freed resources may cause DoS or arbitrary code execution.
1333
1334## new and delete operators must be used in pairs, and new\[] and delete\[] operators must also be used in pairs.
1335
1336**\[Description]**
1337
1338The object created using the new operator can be destroyed only using the delete operator. The object array created using the new\[] operator can be destroyed only using the delete\[] operator.
1339
1340**\[Noncompliant Code Example]**
1341
1342```cpp
1343class C {
1344public:
1345    C(size_t len) : arr(new int[len]) {}
1346    ~C()
1347    {
1348        delete arr; // delete[] arr; should be used.
1349    }
1350
1351private:
1352    int* arr;
1353};
1354```
1355
1356**\[Compliant Code Example]**
1357
1358```cpp
1359class C {
1360public:
1361    C(size_t len) : arr(new int[len]) {}
1362    ~C() { delete[] arr; }
1363
1364private:
1365    int* arr;
1366};
1367```
1368
1369## The custom operators new and delete must be defined in pairs, and the behavior specified in the operators must be the same as that of the operators to be replaced
1370
1371**\[Description]**
1372
1373The custom operators new and delete as well as new\[] and delete\[] must be defined in pairs. The behavior specified in the new/delete operators must be the same as that of the operators to be replaced.
1374
1375**\[Noncompliant Code Example]**
1376
1377```cpp
1378// If the custom operator new is defined, the corresponding operator delete must be defined.
1379struct S {
1380    static void* operator new(size_t sz)
1381    {
1382        ... // Custom operation
1383        return ::operator new(sz);
1384    }
1385};
1386```
1387
1388**\[Compliant Code Example]**
1389
1390```cpp
1391struct S {
1392    static void* operator new(size_t sz)
1393    {
1394        ... // Custom operation
1395        return ::operator new(sz);
1396    }
1397    static void operator delete(void* ptr, size_t sz)
1398    {
1399        ... // Custom operation
1400        ::operator delete(ptr);
1401    }
1402};
1403```
1404
1405The default operator new throws an exception `std::bad_alloc` when memory allocation fails, whereas the operator new that uses the `std::nothrow` parameter returns **nullptr** in the case of a memory allocation failure. The behavior specified the custom operators new and delete must be the same as that of built-in operators.
1406
1407**\[Noncompliant Code Example]**
1408
1409```cpp
1410// Function declared in the header file of the memory management module
1411extern void* AllocMemory(size_t size);   // nullptr is returned in the case of a memory allocation failure.
1412void* operator new(size_t size)
1413{
1414    return AllocMemory(size);
1415}
1416```
1417
1418**\[Compliant Code Example]**
1419
1420```cpp
1421// Function declared in the header file of the memory management module
1422extern void* AllocMemory(size_t size);   // nullptr is returned in the case of a memory allocation failure.
1423void* operator new(size_t size)
1424{
1425    void* ret = AllocMemory(size);
1426    if (ret != nullptr) {
1427        return ret;
1428    }
1429    throw std::bad_alloc();              // An exception is thrown in the case of an allocation failure.
1430}
1431
1432void* operator new(size_t size, const std::nothrow_t& tag)
1433{
1434    return AllocMemory(size);
1435}
1436```
1437
1438# Error Handling
1439
1440## Throw an object itself instead of the pointer to the object when throwing an exception
1441
1442**\[Description]**
1443
1444The recommended exception throwing method in C++ is to throw the object itself instead of the pointer to the object.
1445
1446When the throw statement is used to throw an exception, a temporary object, called an exception object, is constructed. The life cycle of the exception object is clearly defined in the C++ standard: The exception object is constructed when it is thrown. It is destructed when a catch statement of the exception object does not end with `throw` (that is, it is not thrown again) or when the `std::exception_ptr` object that captures the exception is destructed.
1447
1448If a pointer is thrown, the responsibility for recycling the thrown object is unclear. Whether you are obligated to perform the `delete` operation on the pointer where the exception is caught depends on how the object is allocated (for example, static variables, or allocation using `new`) and whether the object is shared. However, the pointer type itself does not indicate the life cycle or ownership of the object, and therefore it is impossible to determine whether the `delete` operation should be performed on the object. If the `delete` operation is not performed on the object that should be deleted, memory leaks occur. If the `delete` operation is performed on the object that should not be deleted, memory will be freed twice.
1449
1450**\[Noncompliant Code Example]**
1451
1452Do not throw pointers.
1453
1454```cpp
1455static SomeException exc1("reason 1");
1456
1457try {
1458    if (SomeFunction()) {
1459        throw &exc1;                         // Noncompliant: This is the pointer to the static object, which should not be deleted.
1460    } else {
1461        throw new SomeException("reason 2"); // Noncompliant: The dynamically allocated object should be deleted.
1462    }
1463} catch (const SomeException* e) {
1464    delete e;                                // Noncompliant: It is uncertain whether the delete operation is required.
1465}
1466```
1467
1468**\[Compliant Code Example]**
1469
1470Always throw the exception object itself.
1471
1472```cpp
1473try {
1474    if (SomeFunction()) {
1475        throw SomeException("reason 1");
1476    } else {
1477        throw SomeException("reason 2");
1478    }
1479} catch (const SomeException& e) {
1480    ...                                      // Compliant. It can be determined that the delete operation is not required.
1481}
1482```
1483
1484## Never throw exceptions from destructors
1485
1486**\[Description]**
1487
1488By default, destructors have the `noexcept` attribute. If they throw exceptions, `std::terminate` will be present. Since C++ 11, destructors can be marked as `noexcept(false)`. However, if a destructor is called during stack unwinding (for example, another exception is thrown, causing local variables on the stack to be destructed), `std::terminate` occurs. The destructors are mostly used to deallocate local variables regardless of whether the return value is normal or whether an exception is thrown. Therefore, it is generally not good to throw exceptions from destructors.
1489
1490# Standard Library
1491
1492## Do not create a std::string from a null pointer
1493
1494**\[Description]**
1495
1496The null pointer is dereferenced when it is passed to the std::string constructor, causing undefined behavior.
1497
1498**\[Noncompliant Code Example]**
1499
1500```cpp
1501void Foo()
1502{
1503    const char* path = std::getenv("PATH");
1504    std::string str(path);                  // Error: No check on whether the return value of getenv is nullptr
1505    std::cout << str << std::endl;
1506}
1507```
1508
1509**\[Compliant Code Example]**
1510
1511```cpp
1512void Foo()
1513{
1514    const char* path = std::getenv("PATH");
1515    if (path == nullptr) {
1516        ... // Error reporting
1517        return;
1518    }
1519    std::string str(path);
1520    ...
1521    std::cout << str << std::endl;
1522}
1523void Foo()
1524{
1525    const char* path = std::getenv("PATH");
1526    std::string str(path == nullptr ? path : "");
1527    ... // Check on the null string
1528    std::cout << str << std::endl;                // Check on the null string if necessary
1529}
1530```
1531
1532## Do not save the pointers returned by the **c\_str()** and **data()** member functions of std::string
1533
1534**\[Description]**
1535
1536To ensure the validity of the reference values returned by the **c\_str()** and **data()** member functions of the std::string object, do not save the **c\_str()** and **data()** results of std::string. Instead, call them directly when needed (the call overhead is optimized through compiler inlining). Otherwise, when the std::string object is modified by calling its modify method, or when the std::string object is out of the scope, the stored pointer becomes invalid. Using an invalid pointer will result in undefined behavior.
1537
1538**\[Noncompliant Code Example]**
1539
1540```cpp
1541void Bar(const char*  data)
1542{
1543    ...
1544}
1545
1546void Foo1()
1547{
1548    std::string name{"demo"};
1549    const char* text = name.c_str();          // After the expression ends, the life cycle of name is still in use and the pointer is valid.
1550
1551    // If a non-const member function (such as operator[] and begin()) of std::string is called and the name is therefore modified,
1552    // the text content may become unavailable or may not be the original character string.
1553    name = "test";
1554    name[1] = '2';
1555    ...
1556    Bar(text);                                // The text no longer points to the valid memory space.
1557}
1558
1559void Foo2()
1560{
1561    std::string name{"demo"};
1562    std::string test{"test"};
1563    const char* text = (name + test).c_str(); // After the expression ends, the temporary object generated by the + operator is destroyed.
1564    ...
1565    Bar(text);                                // The text no longer points to the valid memory space.
1566}
1567
1568void Foo3(std::string& s)
1569{
1570    const char* data = s.data();
1571    ...
1572    s.replace(0, 3, "***");
1573    ...
1574    Bar(data);                                // The data no longer points to the valid memory space.
1575}
1576```
1577
1578**\[Compliant Code Example]**
1579
1580```cpp
1581void Foo1()
1582{
1583    std::string name{"demo"};
1584
1585    name = "test";
1586    name[1] = '2';
1587    ...
1588    Bar(name.c_str());
1589}
1590
1591void Foo2()
1592{
1593    std::string name{"demo"};
1594    std::string test{"test"};
1595    name += test;
1596    ...
1597    Bar(name.c_str());
1598}
1599
1600void Foo3(std::string& s)
1601{
1602    ...
1603    s.replace(0, 3, "***");
1604    ...
1605    Bar(s.data());
1606}
1607```
1608
1609**\[Exception]**
1610
1611In rare cases where high performance coding is required, you can temporarily save the pointer returned by the c\_str() method of std::string to match the existing functions which support only the input parameters of the `const char*` type. However, you should ensure that the life cycle of the std::string object is longer than that of the saved pointer, and that the std::string object is not modified within the life cycle of the saved pointer.
1612
1613## Ensure that the buffer for strings has sufficient space for character data and terminators, and that the string is null-terminated
1614
1615**\[Description]**
1616
1617A C-style character string is a continuous sequence of characters, which is terminated by the first null character and contains the null character.
1618
1619When copying or storing a C-style string, ensure that the buffer has sufficient space to hold the character sequence including the null terminator, and that the string is null terminated. Otherwise, buffer overflows may occur.
1620
1621- Preferentially use std::string to indicate a string because it is easier to operate and more likely to be correctly used. This can prevent overflows and null-terminator missing due to improper use of C-style strings.
1622- When using the C-style strings provided by the C/C++ standard library for function operations, ensure that the input string is null terminated, that the string is not read or written beyond the string buffer, and that the string after the storage operation is null terminated.
1623
1624**\[Noncompliant Code Example]**
1625
1626```cpp
1627char buf[BUFFER_SIZE];
1628std::cin >> buf;
1629void Foo(std::istream& in)
1630{
1631    char buffer[BUFFER_SIZE];
1632    if (!in.read(buffer, sizeof(buffer))) { // Note: in.read() does not ensure null termination.
1633        ... // Error handling
1634        return;
1635    }
1636
1637    std::string str(buffer);                // Noncompliant: The string is not null terminated.
1638    ...
1639}
1640void Foo(std::istream& in)
1641{
1642    std::string s;
1643    in >> s;                    // Noncompliant: The length of the data to be read is not restricted, which may cause resource consumption or attacks.
1644    ...
1645}
1646```
1647
1648**\[Compliant Code Example]**
1649
1650```cpp
1651char buf[BUFFER_SIZE] = {0};
1652std::cin.width(sizeof(buf) - 1); // The buffer length must be N–1 to reserve space for a null terminator.
1653std::cin >> buf;
1654void Foo(std::istream& in)
1655{
1656    char buffer[BUFFER_SIZE];
1657
1658    if (!in.read(buffer, sizeof(buffer)) { // Note: in.read() does not ensure null termination.
1659        ... // Error handling
1660        return;
1661    }
1662
1663    std::string str(buffer, in.gcount()); // Ensure that the std::string constructor reads only characters of a specified length.
1664    ...
1665}
1666void Foo(std::istream& in)
1667{
1668    std::string s;
1669    in.width(MAX_NEED_SIZE);
1670    in >> s;                             // Compliant: The maximum length of the data to be read is restricted.
1671    ...
1672}
1673```
1674
1675**\[Impact]**
1676
1677Setting no limits to the integer values in external data is likely to cause DoS, buffer overflows, information leaks, or arbitrary code execution.
1678
1679## Do not use std::string to store sensitive information
1680
1681**\[Description]**
1682
1683The std::string class is a string management class defined in C++. If sensitive information (such as passwords) is operated using std::string, it may be scattered in memory during program running and cannot be cleared.
1684
1685**\[Noncompliant Code Example]**
1686
1687In the following code example, the **Foo()** function obtains the password, saves it to the std::string variable **password**, and then passes it to the **VerifyPassword()** function. In this process, two copies of the password exist in memory.
1688
1689```cpp
1690bool VerifyPassword(std::string password)
1691{
1692    ...
1693}
1694
1695void Foo()
1696{
1697    std::string password = GetPassword();
1698    VerifyPassword(password);
1699}
1700```
1701
1702**\[Impact]**
1703
1704Sensitive information is not deleted in due time, which may cause information leaks.
1705
1706## Ensure that the external data used as container indexes or iterators is within the valid range
1707
1708**\[Description]**
1709
1710External data is untrusted. When it is used as container or array indexes, ensure that its value is within the valid range of the elements that can be accessed by containers or arrays. When external data is used for iterator offset, ensure that the iterator offset value range is \[begin(), end()] of the container associated with the iterator (created from the begin() method of the container object c), that is, greater than or equal to c.begin() and less than or equal to c.end().
1711
1712For a container (such as std::vector, std::set, or std::map) with the at() method, if the corresponding index is out of range or the key-value does not exist, the method throws an exception. If the index of the corresponding operator\[] is out of range, undefined behavior occurs. If the default key-value fails to be constructed when the corresponding key-value does not exist, undefined behavior also occurs.
1713
1714**\[Noncompliant Code Example]**
1715
1716```cpp
1717int main()
1718{
1719    // Obtain an integer (index) from external input.
1720    int index;
1721    if (!(std::cin >> index)) {
1722        ... // Error handling
1723        return -1;
1724    }
1725
1726    std::vector<char> c{'A', 'B', 'C', 'D'};
1727
1728    // Noncompliant: The index range is not correctly verified, causing out-of-bounds read: Ensure that the index is within the range of the container element.
1729    std::cout << c[index] << std::endl;
1730
1731    // Noncompliant: Ensure that the index is within the range of the container or array element.
1732    for (auto pos = std::cbegin(c) + index; pos < std::cend(c); ++pos) {
1733        std::cout << *pos << std::endl;
1734    }
1735    return 0;
1736}
1737void Foo(size_t n)
1738{
1739    std::vector<int> v{0, 1, 2, 3};
1740
1741    // n is the index transferred through an external API, which may cause out-of-bounds access.
1742    for_each_n(v.cbegin(), n, [](int x) { std::cout << x; });
1743}
1744```
1745
1746**\[Compliant Code Example]**
1747
1748```cpp
1749int main()
1750{
1751    // Obtain an integer (index) from external input.
1752    int index;
1753    if (!(std::cin >> index)) {
1754        ... // Error handling
1755        return -1;
1756    }
1757
1758    // std::vector is used as an example. Code such as std::cbegin(c) also applies to std::string
1759    // and C array (not applicable to the char* variable and the static character string represented by char*).
1760    std::vector<char> c{'A', 'B', 'C', 'D'};
1761
1762    try {
1763        std::cout << c.at(index) << std::endl; // Compliant: When the index is out of range, the at() function throws an exception
1764    } catch (const std::out_of_range& e) {
1765        ... // Out-of-bounds exception handling
1766    }
1767
1768    // In subsequent code, the valid index must be used for container element index or iterator offset.
1769    // The index range is correctly verified: The index is within the range of the container element.
1770    if (index < 0 || index >= c.size()) {
1771        ... // Error handling
1772        return -1;
1773    }
1774
1775    std::cout << c[index] << std::endl;        // Compliant: The index range has been validated.
1776
1777    // Compliant: The index has been validated.
1778    for (auto pos = std::cbegin(c) + index; pos < std::cend(c); ++pos) {
1779        std::cout << *pos << std::endl;
1780    }
1781    return 0;
1782}
1783void Foo(size_t n)
1784{
1785    std::vector<int> v{0, 1, 2, 3};
1786
1787    // Ensure that the iteration range [first, first + count) of for_each_n is valid.
1788    if (n > v.size()) {
1789        ... // Error handling
1790        return;
1791    }
1792    for_each_n(v.cbegin(), n, [](int x) { std::cout << x; });
1793}
1794```
1795
1796## Use valid format strings when calling formatted input/output functions
1797
1798**\[Description]**
1799
1800When using C-style formatted input/output functions, ensure that the format strings are valid and strictly match the corresponding parameter types. Otherwise, unexpected behavior occurs.
1801
1802In addition to C-style formatted input/output functions, similar functions in C must also use valid format strings, for example, the **std::format()** function in C++20.
1803
1804For a custom C-style formatted function, you can use the attributes supported by the compiler to automatically check its correctness. For example, the GCC can automatically check custom formatted functions (such as printf, scanf, strftime, and strfmon). For details, see Common Function Attributes in the GCC manual:
1805
1806```c
1807extern int CustomPrintf(void* obj, const char* format, ...)
1808    __attribute__ ((format (printf, 2, 3)));
1809```
1810
1811**\[Noncompliant Code Example]**
1812
1813In the following code example, an integer is formatted into the macAddr variable, but macAddr is of the unsigned char type, and %x indicates a parameter of the int type. After the function is executed, out-of-bounds write occurs.
1814
1815```c
1816unsigned char macAddr[6];
1817...
1818// The data format in macStr is e2:42:a4:52:1e:33.
1819int ret = sscanf(macStr, "%x:%x:%x:%x:%x:%x\n",
1820                  &macAddr[0], &macAddr[1],
1821                  &macAddr[2], &macAddr[3],
1822                  &macAddr[4], &macAddr[5]);
1823...
1824```
1825
1826**\[Compliant Code Example]**
1827
1828In the following code example, %hhx is used to ensure that the format string strictly matches the parameter type.
1829
1830```c
1831unsigned char macAddr[6];
1832...
1833// The data format in macStr is e2:42:a4:52:1e:33.
1834int ret = sscanf(macStr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\n",
1835                  &macAddr[0], &macAddr[1],
1836                  &macAddr[2], &macAddr[3],
1837                  &macAddr[4], &macAddr[5]);
1838...
1839```
1840
1841Note: It is not advised to use C library functions, such as **sscanf()** and **sprintf()**, in C++. You can use std::istringstream, std::ostringstream, and std::stringstream instead.
1842
1843**\[Impact]**
1844
1845An incorrect format string may cause memory damage or abnormal program termination.
1846
1847## Ensure that the format parameter is not controlled by external data when a formatted input/output function is called
1848
1849**\[Description]**
1850
1851When a formatted function is called, the **format** parameter provided or concatenated by external data will cause a string formatting vulnerability. Take the formatted output function of the C standard library as an example. When the **format** parameter is externally controllable, an attacker can use the %n converter to write an integer to a specified address, use the %x or %d converter to view the stack or register content, or use the %s converter to cause process crashes or other issues.
1852
1853Common formatted functions are as follows:
1854
1855- Formatted output functions: **sprintf()**, **vsprintf()**, **snprintf()**, **vsnprintf()**, etc.
1856- Formatted input functions: **sscanf()**, **vsscanf()**, **fscanf()**, **vscanf()**, etc.
1857- Formatted error message functions: **err()**, **verr()**, **errx()**, **verrx()**, **warn()**, **vwarn()**, **warnx()**, **vwarnx()**, **error()**, and **error\_at\_line()**
1858- Formatted log functions: **syslog()** and **vsyslog()**
1859- **std::format()** provided by C++20
1860
1861When a formatted function is called, the constant string should be used as the format string. The format string must not be externally controllable:
1862
1863```cpp
1864Box<int> v{MAX_COUNT};
1865std::cout << std::format("{:#x}", v);
1866```
1867
1868**\[Noncompliant Code Example]**
1869
1870In the following code example, the **Log()** function is used to directly log external data, which may cause a format string vulnerability.
1871
1872```c
1873void Foo()
1874{
1875    std::string msg = GetMsg();
1876    ...
1877    syslog(priority, msg.c_str());       // Noncompliant: A format string vulnerability exists.
1878}
1879```
1880
1881**\[Compliant Code Example]**
1882The following practice is recommended: Use the %s converter to log external data to avoid the format string vulnerability.
1883
1884```c
1885void Foo()
1886{
1887    std::string msg = GetMsg();
1888    ...
1889    syslog(priority, "%s", msg.c_str()); // Compliant: No format string vulnerability exists.
1890}
1891```
1892
1893**\[Impact]**
1894
1895If the format string is externally controllable, attackers can crash the process, view the stack content, view the memory content, or write data to any memory location, and then execute any code with the permission of the compromised process.
1896
1897## Do not use external controllable data as parameters for process startup functions or for the loading functions of dlopen/LoadLibrary and other modules
1898
1899**\[Description]**
1900
1901Process startup functions in this requirement include **system()**, **popen()**, **execl()**, **execlp()**, **execle()**, **execv()**, and **execvp()**. Each of these functions such as **system()** and **popen()** will create a process. If external controllable data is used as the parameters of these functions, injection vulnerabilities may occur. When functions such as **execl()** are used to execute new processes, command injection risks also exist if shell is used to start new processes. The use of **execlp()**, **execvp()**, and **execvpe()** functions depends on the program paths searched using the system environment variable PATH. Consider the risks of external environment variables when using these functions, or avoid using these functions.
1902
1903Therefore, C standard functions are always preferred to implement the required functions. If you need to use these functions, use the trustlist mechanism to ensure that the parameters of these functions are not affected by any external data.
1904
1905The **dlopen()** and **LoadLibrary()** functions load external modules. If external controllable data is used as parameters of these functions, the modules prepared by attackers may be loaded. If these functions need to be used, take one of the following measures:
1906
1907- Use the trustlist mechanism to ensure that the parameters of these functions are not affected by any external data.
1908- Use the digital signature mechanism to protect the modules to be loaded, ensuring their integrity.
1909- After the security of the dynamic library loaded locally is ensured by means of permission and access control, the dynamic library is automatically loaded using a specific directory.
1910- After the security of the local configuration file is ensured by means of permission and access control, the dynamic library specified in the configuration file is automatically loaded.
1911
1912**\[Noncompliant Code Example]**
1913
1914In the following code example, external controllable data is directly used as the parameter of the **LoadLibrary()** function, which may implant Trojan horses into the program.
1915
1916```c
1917char* msg = GetMsgFromRemote();
1918LoadLibrary(msg);
1919```
1920
1921In the following code example, the external **cmd** command is executed by the **system()** function. Attackers can execute any command:
1922
1923```c
1924std::string cmd = GetCmdFromRemote();
1925system(cmd.c_str());
1926```
1927
1928In the following code example, a part of the **cmd** command executed by the **system()** function is external data. An attacker may enter `some dir;reboot` to cause system reboot:
1929
1930```cpp
1931std::string name = GetDirNameFromRemote();
1932std::string cmd{"ls " + name};
1933system(cmd.c_str());
1934```
1935
1936When using **exec()** functions to prevent command injection, do not use command parsers (such as **/bin/sh**) for the **path** and **file** parameters in the functions.
1937
1938```c
1939int execl(const char* path, const char* arg, ...);
1940int execlp(const char* file, const char* arg, ...);
1941int execle(const char* path, const char* arg, ...);
1942int execv(const char* path, char* const argv[]);
1943int execvp(const char* file, char* const argv[]);
1944int execvpe(const char* file, char* const argv[], char* const envp[]);
1945```
1946
1947For example, do not use the following methods:
1948
1949```c
1950std::string cmd = GetDirNameFromRemote();
1951execl("/bin/sh", "sh", "-c", cmd.c_str(), nullptr);
1952```
1953
1954You can use library functions or write a small amount of code to avoid using the **system()** function to call commands. For example, the `mkdir()` function can implement the function of the `mkdir` command. In the following code, avoid using the `cat` command to copy file content.
1955
1956```c
1957int WriteDataToFile(const char* dstFile, const char* srcFile)
1958{
1959    ...  // Argument validation
1960    std::ostringstream oss;
1961    oss << "cat " << srcFile << " > " << dstFile;
1962
1963    std::string cmd{oss.str()};
1964    system(cmd.c_str());
1965    ...
1966}
1967```
1968
1969**\[Compliant Code Example]**
1970
1971Some command functions can be implemented through a small amount of coding. The following code implements the file copy function to avoid calling the `cat` or `cp` command. Note that the following code does not consider the impact of signal interruption for easy description.
1972
1973```cpp
1974bool WriteDataToFile(const std::string& dstFilePath, const std::string& srcFilePath)
1975{
1976    const int bufferSize = 1024;
1977    std::vector<char> buffer (bufferSize + 1, 0);
1978
1979    std::ifstream srcFile(srcFilePath, std::ios::binary);
1980    std::ofstream dstFile(dstFilePath, std::ios::binary);
1981
1982    if (!dstFile || !dstFile) {
1983        ... // Error handling
1984        return false;
1985    }
1986
1987    while (true) {
1988        // Read the block content from srcFile.
1989        srcFile.read(buffer.data(), bufferSize);
1990        std::streamsize size = srcFile ? bufferSize : srcFile.gcount();
1991
1992        // Write the block content to dstFile.
1993        if (size > 0 && !dstFile.write(buffer.data(), size)) {
1994            ... // Error handling
1995            break;
1996        }
1997
1998        if (!srcFile) {
1999            ... // Error check: An error is recorded before eof() is returned.
2000            break;
2001        }
2002    }
2003    // srcFile and dstFile are automatically closed when they exit the scope.
2004    return true;
2005}
2006```
2007
2008Avoid calling the command processor to execute external commands if functionality can be implemented by using library functions (as shown in the preceding example). If a single command needs to be called, use the **exec\*** function for parameterized calling and implement trustlist management on the called command. In addition, avoid using the **execlp()**, **execvp()**, and **execvpe()** functions because these functions depend on the external PATH environment variable. In this case, the externally input **fileName** is only used as a parameter of the **some\_tool** command, posing no command injection risks.
2009
2010```cpp
2011pid_t pid;
2012char* const envp[] = {nullptr};
2013...
2014std::string fileName = GetDirNameFromRemote();
2015...
2016pid = fork();
2017if (pid < 0) {
2018    ...
2019} else if (pid == 0) {
2020    // Use some_tool to process the specified file.
2021    execle("/bin/some_tool", "some_tool", fileName.c_str(), nullptr, envp);
2022    _Exit(-1);
2023}
2024...
2025int status;
2026waitpid(pid, &status, 0);
2027std::ofstream ofs(fileName, std::ios::in);
2028...
2029```
2030
2031When the system command parsers (such as system) must be used to execute commands, the entered command strings must be checked based on an appropriate trustlist to prevent command injection.
2032
2033```cpp
2034std::string cmd = GetCmdFromRemote();
2035
2036// Use the trustlist to check whether the command is valid. Only the "some_tool_a" and "some_tool_b" commands are allowed, and external control is not allowed.
2037if (!IsValidCmd(cmd.c_str())) {
2038    ... // Error handling
2039}
2040system(cmd.c_str());
2041...
2042```
2043
2044**\[Impact]**
2045
2046- If the command string passed to **system()**, **popen()**, or other command handler functions is externally controllable, an attacker may execute any command that exists in the system using the permission of the compromised process.
2047- If a dynamic library file is externally controllable, attackers can replace the dynamic library file, which may cause arbitrary code execution vulnerabilities in some cases.
2048
2049# Other C Coding Specifications
2050
2051## Do not apply the sizeof operator to function parameters of array type when taking the size of an array
2052
2053**\[Description]**
2054
2055The **sizeof** operator yields the size (in bytes) of its operand, which can be an expression or the parenthesized name of a type, for example, `sizeof(int)` or `sizeof(int *)`. Footnote 103 in section 6.5.3.4 of the C11 standard states that:
2056
2057> When applied to a parameter declared to have array or function type, the **sizeof** operator yields the size of the adjusted (pointer) type.
2058
2059Arguments declared as arrays in the argument list will be adjusted to pointers of corresponding types. For example, although the inArray argument in the `void Func(int inArray[LEN])` function is declared as an array, it is actually adjusted to an int pointer, that is, `void Func(int *inArray)`. As a result, `sizeof(inArray)` is equal to `sizeof(int *)` in this function, leading to unexpected result. For example, in the IA-32 architecture, `sizeof(inArray)` is 4, not the inArray size.
2060
2061**\[Noncompliant Code Example]**
2062
2063In the following code example, the **ArrayInit()** function is used to initialize array elements. This function has a parameter declared as `int inArray[]`. When this function is called, a 256-element integer array **data** is passed to it. The **ArrayInit()** function uses `sizeof(inArray) / sizeof(inArray[0])` to determine the number of elements in the input array. However, **inArray** is a function parameter and therefore has a pointer type. As a result, `sizeof(inArray)` is equal to `sizeof(int *)`. The expression `sizeof(inArray) / sizeof(inArray[0])` evaluates to 1, regardless of the length of the array passed to the **ArrayInit()** function, leading to unexpected behavior.
2064
2065```c
2066#define DATA_LEN 256
2067void ArrayInit(int inArray[])
2068{
2069    // Noncompliant: sizeof(inArray) is used to calculate the array size.
2070    for (size_t i = 0; i < sizeof(inArray) / sizeof(inArray[0]); i++) {
2071        ...
2072    }
2073}
2074
2075void FunctionData(void)
2076{
2077    int data[DATA_LEN];
2078
2079    ...
2080    ArrayInit(data); // Call ArrayInit() to initialize array data.
2081    ...
2082}
2083```
2084
2085**\[Compliant Code Example]**
2086
2087In the following code example, the function definition is modified, an array size parameter is added, and the array size is correctly passed to the function.
2088
2089```c
2090#define DATA_LEN 256
2091// Function description: Argument len is the length of inArray.
2092void ArrayInit(int inArray[], size_t len)
2093{
2094    for (size_t i = 0; i < len; i++) {
2095        ...
2096    }
2097}
2098
2099void FunctionData(void)
2100{
2101    int data[DATA_LEN];
2102
2103    ArrayInit(data, sizeof(data) / sizeof(data[0]));
2104    ...
2105}
2106```
2107
2108**\[Noncompliant Code Example]**
2109
2110In the following code example, `sizeof(inArray)` does not equal `ARRAY_MAX_LEN * sizeof(int)`, because the **sizeof** operator, when applied to a parameter declared to have array type, yields the size of the adjusted (pointer) type even if the parameter declaration specifies a length. In this case, `sizeof(inArray)` is equal to `sizeof(int *)`.
2111
2112```c
2113#define ARRAY_MAX_LEN 256
2114
2115void ArrayInit(int inArray[ARRAY_MAX_LEN])
2116{
2117    // Noncompliant: sizeof(inArray), pointer size rather than array size, which is not as expected
2118    for (size_t i = 0; i < sizeof(inArray) / sizeof(inArray[0]); i++) {
2119        ...
2120    }
2121}
2122
2123int main(void)
2124{
2125    int masterArray[ARRAY_MAX_LEN];
2126
2127    ...
2128    ArrayInit(masterArray);
2129
2130    return 0;
2131}
2132```
2133
2134**\[Compliant Code Example]**
2135
2136In the following code example, the specified array length is indicated by the **len** argument.
2137
2138```c
2139#define ARRAY_MAX_LEN 256
2140
2141// Function description: Argument len is the length of the argument array.
2142void ArrayInit(int inArray[], size_t len)
2143{
2144    for (size_t i = 0; i < len; i++) {
2145        ...
2146    }
2147}
2148
2149int main(void)
2150{
2151    int masterArray[ARRAY_MAX_LEN];
2152
2153    ArrayInit(masterArray, ARRAY_MAX_LEN);
2154    ...
2155
2156    return 0;
2157}
2158```
2159
2160## Do not perform the **sizeof** operation on pointer variables to obtain the array size
2161
2162**\[Description]**
2163
2164Performing the **sizeof** operation on a pointer that is used as an array leads to an unexpected result. For example, in the variable definition `char *p = array` where array is defined as `char array[LEN]`, the result of the expression `sizeof(p)` is the same as that of `sizeof(char *)`, but not the size of the array.
2165
2166**\[Noncompliant Code Example]**
2167
2168In the following code example, **buffer** is a pointer while **path** is an array. The programmer wants to clear the two memory segments. However, the programmer mistakenly writes the memory size as `sizeof(buffer)`, leading to an unexpected result.
2169
2170```c
2171char path[MAX_PATH];
2172char *buffer = (char *)malloc(SIZE);
2173...
2174
2175...
2176memset(path, 0, sizeof(path));
2177
2178// sizeof causes an unexpected result because its result will be the pointer size but not the buffer size.
2179memset(buffer, 0, sizeof(buffer));
2180```
2181
2182**\[Compliant Code Example]**
2183
2184In the following code example, `sizeof(buffer)` is changed to the size of the requested buffer:
2185
2186```c
2187char path[MAX_PATH];
2188char *buffer = (char *)malloc(SIZE);
2189...
2190
2191...
2192memset(path, 0, sizeof(path));
2193memset(buffer, 0, SIZE); // Use the requested buffer size.
2194```
2195
2196## Do not directly use external data to concatenate SQL statements
2197
2198**\[Description]**
2199
2200An SQL injection vulnerability arises when an SQL query is dynamically altered to form an altogether different query. Execution of this altered query may result in information leaks or data tampering. The root cause of SQL injection is the use of external data to concatenate SQL statements. In C/C++, external data is used to concatenate SQL statements in the following scenarios (but not limited to):
2201
2202- Argument for calling **mysql\_query()** and **Execute()** when connecting to MySQL
2203- Argument for calling **dbsqlexec()** of the db-library driver when connecting to the SQL server
2204- SQL statement parameter for calling **SQLprepare()** of the ODBC driver when connecting to the database
2205- Argument for calling **otl\_stream()** and **otl\_column\_desc()** in OTL class library in C++ language
2206- Input argument for calling **ExecuteWithResSQL()** when connecting to the Oracle database in C++ language
2207
2208The following methods can be used to prevent SQL injection:
2209
2210- Use parameterized query (also known as a prepared statement): Parameterized query is a simple and effective way to prevent SQL injection and must be used preferentially. The databases MySQL and Oracle (OCI) support parameterized query.
2211- Use parameterized query (driven by ODBC): supported by Oracle, SQL server, PostgreSQL, and GaussDB databases.
2212- Verify external data (trustlist verification is recommended).
2213- Escape external SQL special characters.
2214
2215**\[Noncompliant Code Example]**
2216
2217The following code concatenates user input without checking the input, causing SQL injection risks:
2218
2219```c
2220char name[NAME_MAX];
2221char sqlStatements[SQL_CMD_MAX];
2222int ret = GetUserInput(name, NAME_MAX);
2223...
2224ret = sprintf(sqlStatements,
2225                "SELECT childinfo FROM children WHERE name= ‘%s’",
2226                name);
2227...
2228ret = mysql_query(&myConnection, sqlStatements);
2229...
2230```
2231
2232**\[Compliant Code Example]**
2233
2234Use prepared statements for parameterized query to defend against SQL injection attacks:
2235
2236```c
2237char name[NAME_MAX];
2238...
2239MYSQL_STMT *stmt = mysql_stmt_init(myConnection);
2240char *query = "SELECT childinfo FROM children WHERE name= ?";
2241if (mysql_stmt_prepare(stmt, query, strlen(query))) {
2242    ...
2243}
2244int ret = GetUserInput(name, NAME_MAX);
2245...
2246MYSQL_BIND params[1];
2247(void)memset(params, 0, sizeof(params));
2248...
2249params[0].bufferType = MYSQL_TYPE_STRING;
2250params[0].buffer = (char *)name;
2251params[0].bufferLength = strlen(name);
2252params[0].isNull = 0;
2253
2254bool isCompleted = mysql_stmt_bind_param(stmt, params);
2255...
2256ret = mysql_stmt_execute(stmt);
2257...
2258```
2259
2260**\[Impact]**
2261
2262If the string of a concatenated SQL statement is externally controllable, attackers can inject specific strings to deceive programs into executing malicious SQL commands, causing information leakage, permission bypass, and data tampering.
2263
2264## Clear sensitive information from memory immediately after using it
2265
2266**\[Description]**
2267
2268Sensitive information (such as passwords and keys) in memory must be cleared immediately after being used to prevent it from being obtained by attackers or accidentally disclosed to low-privilege users. Memory includes but is not limited to:
2269
2270- Dynamically allocated memory
2271- Statically allocated memory
2272- Automatically allocated (stack) memory
2273- Memory cache
2274- Disk cache
2275
2276**\[Noncompliant Code Example]**
2277
2278Generally, memory data does not need to be cleared before memory resources are released to prevent extra overheads during running. Therefore, after memory resources are released, the data remains in memory. In this case, sensitive information in the data may be leaked accidentally. To prevent sensitive information leakage, you must clear sensitive information from memory before releasing memory resources. In the following code example, the sensitive information **secret** stored in the referenced dynamic memory is copied to the newly dynamically allocated buffer **newSecret**, and is finally released through **free()**. As data is not cleared before the memory block is released, the memory block may be reallocated to another part of the program, and sensitive information stored in **newSecret** may be accidentally disclosed.
2279
2280```c
2281char *secret = NULL;
2282/*
2283 * Assume that secret points to sensitive information whose content is less than SIZE_MAX
2284 * and ends with null.
2285 */
2286
2287size_t size = strlen(secret);
2288char *newSecret = NULL;
2289newSecret = (char *)malloc(size + 1);
2290if (newSecret == NULL) {
2291    ... // Error handling
2292} else {
2293    errno_t ret = strcpy(newSecret, secret);
2294    ... // Process ret
2295
2296    ... // Process newSecret...
2297
2298    free(newSecret);
2299    newSecret = NULL;
2300}
2301...
2302```
2303
2304**\[Compliant Code Example]**
2305
2306In the following code example, to prevent information leakage, clear the dynamic memory that contains sensitive information (by filling the memory space with '\\0') and then release it.
2307
2308```c
2309char *secret = NULL;
2310/*
2311 * Assume that secret points to sensitive information whose content is less than SIZE_MAX
2312 * and ends with null.
2313 */
2314size_t size = strlen(secret);
2315char *newSecret = NULL;
2316newSecret = (char *)malloc(size + 1);
2317if (newSecret == NULL) {
2318    ... // Error handling
2319} else {
2320    errno_t ret = strcpy(newSecret,  secret);
2321    ... // Process ret
2322
2323    ... // Process newSecret...
2324
2325    (void)memset(newSecret,  0, size + 1);
2326    free(newSecret);
2327    newSecret = NULL;
2328}
2329...
2330```
2331
2332**\[Compliant Code Example]**
2333
2334The following code is another scenario involving sensitive information clearance. After obtaining the password, the code saves the password to **password** for verification. After the password is used, `memset()` is used to clear the password.
2335
2336```c
2337int Foo(void)
2338{
2339    char password[MAX_PWD_LEN];
2340    if (!GetPassword(password, sizeof(password))) {
2341        ...
2342    }
2343    if (!VerifyPassword(password)) {
2344        ...
2345    }
2346    ...
2347    (void)memset(password,  0, sizeof(password));
2348    ...
2349}
2350```
2351
2352**NOTE**: Ensure that the code for clearing sensitive information is always valid even if the compiler has been optimized.
2353
2354For example, the following code uses an invalid statement in the optimized compiler.
2355
2356```c
2357int SecureLogin(void)
2358{
2359    char pwd[PWD_SIZE];
2360    if (RetrievePassword(pwd, sizeof(pwd))) {
2361        ... // Password check and other processing
2362    }
2363    memset(pwd, 0, sizeof(pwd)); // Compiler optimizations may invalidate this statement.
2364    ...
2365}
2366```
2367
2368Some compilers do not execute the code during optimization if they consider the code do not change program execution results. Therefore, the **memset()** function may become invalid after optimization.
2369
2370If the compiler supports the **#pragma** instruction, the instruction can be used to instruct the compiler not to optimize.
2371
2372```c
2373void SecureLogin(void)
2374{
2375    char pwd[PWD_SIZE];
2376    if (RetrievePassword(pwd, sizeof(pwd))) {
2377        ... // Password check and other processing
2378    }
2379    #pragma optimize("", off)
2380    // Clear memory.
2381    ...
2382    #pragma optimize("", on)
2383    ...
2384}
2385```
2386
2387**\[Impact]**
2388
2389Failure to rapidly clear sensitive information may cause information leakage.
2390
2391## Create files with appropriate access permissions explicitly specified
2392
2393**\[Description]**
2394
2395If no appropriate access permissions are explicitly specified when a file is created, unauthorized users may access the file, causing information leakage, file data tampering, and malicious code injection into the file.
2396
2397Although file access permissions depend on the file system, many file creation functions (POSIX **open()** functions, etc.) provide mechanisms to set (or influence) file access permissions. Therefore, when these functions are used to create files, appropriate file access permissions must be explicitly specified to prevent unintended access.
2398
2399**\[Noncompliant Code Example]**
2400
2401The POSIX **open()** function is used to create a file but the access permission for the file is not specified, which may cause the file to be created with excessive access permissions. This may lead to vulnerabilities (e.g. CVE-2006-1174).
2402
2403```c
2404void Foo(void)
2405{
2406    int fd = -1;
2407    char *filename = NULL;
2408
2409    ... // Initialize filename.
2410
2411    fd = open(filename, O_CREAT | O_WRONLY); // Access permission not explicitly specified
2412    if (fd == -1) {
2413        ... // Error handling
2414    }
2415    ...
2416}
2417```
2418
2419**\[Compliant Code Example]**
2420
2421Access permissions for the newly created file should be explicitly specified in the third argument to **open()**. Access permissions for a file can be set based on actual applications of the file.
2422
2423```c
2424void Foo(void)
2425{
2426    int fd = -1;
2427    char *filename = NULL;
2428
2429    ... // Initialize filename and specify its access permissions.
2430
2431    // Explicitly specify necessary access permissions for a file.
2432    int fd = open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
2433    if (fd == -1) {
2434        ... // Error handling
2435    }
2436    ...
2437}
2438```
2439
2440**\[Impact]**
2441
2442Creating files with weak access permissions may cause unauthorized access to these files.
2443
2444## Canonicalize and validate file paths before using them
2445
2446**\[Description]**
2447
2448File paths from external data must be validated. Otherwise, system files may be accessed randomly. However, direct validation is not allowed. The file paths must be canonicalized before validation because a file can be described and referenced by paths in various forms. For example, a file path can be an absolute path or a relative path, and the path name, directory name, or file name may contain characters (for example, "." or "..") that make validation difficult and inaccurate. In addition, the file may also be a symbolic link, which further obscures the actual location or identity of the file, making validation difficult and inaccurate. Therefore, file paths must be canonicalized to make it much easier to validate a path, directory, or file name, thereby improving validation accuracy.
2449
2450Because the canonical form may vary with operating systems and file systems, it is best to use a canonical form consistent with the current system features.
2451
2452Take an example as follows:
2453
2454```c
2455Canonicalize file paths coming from external data. Without canonicalization, attackers have chances to construct file paths for unauthorized access to files.
2456For example, an attacker can construct "../../../etc/passwd" to access any file.
2457```
2458
2459**\[Noncompliant Code Example]**
2460
2461In this noncompliant code example, **inputFilename** contains a file name that originates from a tainted source, and the file is opened for writing. Before this file name is used for file operations, it should be validated to ensure that it references an expected and valid file. Unfortunately, the file name referenced by **inputFilename** may contain special characters, such as directory characters, which make validation difficult or even impossible. In addition, **inputFilename** may contain a symbolic link to any file path. Even if the file name passes validation, it is invalid. In this scenario, even if the file name is directly validated, the expected result cannot be obtained. The call to **fopen()** may result in unintended access to a file.
2462
2463```c
2464...
2465
2466if (!verify_file(inputFilename) {    // inputFilename is validated without being canonicalized.
2467    ... // Error handling
2468}
2469
2470if (fopen(inputFilename, "w") == NULL) {
2471    ... // Error handling
2472}
2473
2474...
2475```
2476
2477**\[Compliant Code Example]**
2478
2479Canonicalizing file names is difficult because it requires an understanding of the underlying file system. The POSIX **realpath()** function can help convert path names to a canonical form. According to Standard for Information Technology—Portable Operating System Interface (POSIX®), Base Specifications, Issue 7 \[IEEE Std 1003.1:2013]:
2480
2481- The **realpath()** function shall derive, from the pathname pointed to by **filename**, an absolute pathname that names the same file, whose resolution does not involve a dot (.), double dots (..), or symbolic links. Further verification, such as ensuring that two consecutive slashes or special files do not appear in the file name, must be performed after canonicalization. For more information about how to perform path name resolution, see section 4.12 "Pathname Resolution" of IEEE Std 1003.1:2013. There are many precautions for using the **realpath()** function.  With an understanding of the preceding principles, the following solution can be taken to address the noncompliant code example.
2482
2483```c
2484char *realpathRes = NULL;
2485
2486...
2487
2488// Canonicalize inputFilename before validation.
2489realpathRes = realpath(inputFilename, NULL);
2490if (realpathRes == NULL) {
2491    ... // Canonicalization error handling
2492}
2493
2494// Validate the file path after canonicalizing it
2495if (!verify_file(realpathRes) {
2496    ... // Validation error handling
2497}
2498
2499// Use
2500if (fopen(realpathRes, "w") == NULL) {
2501    ... // Operation error handling
2502}
2503
2504...
2505
2506free(realpathRes);
2507realpathRes = NULL;
2508...
2509```
2510
2511**\[Compliant Code Example]**
2512
2513Based on the actual scenario, a second solution can also be used. The description is as follows: If `PATH_MAX` is defined as a constant in **limits.h**, it is also safe to call **realpath()** with a non-null `resolved_path` value. In this example, the **realpath()** function expects `resolved_path` to refer to a character array that is large enough to hold the canonicalized path. If **PATH\_MAX** is defined, allocate a buffer of size `PATH_MAX` to hold the result of **realpath()**. The compliant code example is as follows:
2514
2515```c
2516char *realpathRes = NULL;
2517char *canonicalFilename = NULL;
2518size_t pathSize = 0;
2519
2520...
2521
2522pathSize = (size_t)PATH_MAX;
2523
2524if (VerifyPathSize(pathSize)) {
2525    canonicalFilename = (char *)malloc(pathSize);
2526
2527    if (canonicalFilename == NULL) {
2528        ... // Error handling
2529    }
2530
2531    realpathRes = realpath(inputFilename, canonicalFilename);
2532}
2533
2534if (realpathRes == NULL) {
2535    ... // Error handling
2536}
2537
2538if (VerifyFile(realpathRes)) {
2539    ... // Error handling
2540}
2541
2542if (fopen(realpathRes, "w") == NULL ) {
2543    ... // Error handling
2544}
2545
2546...
2547
2548free(canonicalFilename);
2549canonicalFilename = NULL;
2550...
2551```
2552
2553**\[Noncompliant Code Example]**
2554
2555The following code obtains file names from external data, concatenates them into a file path, and directly reads the file content. As a result, attackers can read the content of any file.
2556
2557```c
2558char *filename = GetMsgFromRemote();
2559...
2560int ret = sprintf(untrustPath,  "/tmp/%s", filename);
2561...
2562char *text = ReadFileContent(untrustPath);
2563```
2564
2565**\[Compliant Code Example]**
2566
2567Canonicalize the file path and then check whether the path is valid in the program.
2568
2569```c
2570char *filename = GetMsgFromRemote();
2571...
2572sprintf(untrustPath,  "/tmp/%s", filename);
2573char path[PATH_MAX];
2574if (realpath(untrustPath, path) == NULL) {
2575    ... // Error handling
2576}
2577if (!IsValidPath(path)) {    // Check whether the file path is valid.
2578    ... // Error handling
2579}
2580char *text = ReadFileContent(path);
2581```
2582
2583**\[Exception]**
2584
2585File paths can be manually entered on the console where the command line program runs.
2586
2587```c
2588int main(int argc, char **argv)
2589{
2590    int fd = -1;
2591
2592    if (argc == 2) {
2593        fd = open(argv[1], O_RDONLY);
2594        ...
2595    }
2596
2597    ...
2598    return 0;
2599}
2600```
2601
2602**\[Impact]**
2603
2604Failure to canonicalize and validate untrusted file paths may cause access to any file.
2605
2606## Do not create temporary files in shared directories
2607
2608**\[Description]**
2609
2610A shared directory refers to a directory that can be accessed by non-privileged users. The temporary files of a program must be exclusively used by the program. If you place the temporary files of the program in the shared directory, other sharing users may obtain additional information about the program, resulting in information leakage. Therefore, do not create temporary files that are used only by a program itself in any shared directory.
2611
2612Temporary files are commonly used for auxiliary storage of data that cannot reside in memory or temporary data and also as a means of inter-process communication (by transmitting data through the file system). For example, one process creates a temporary file with a well-known name or a temporary name in a shared directory. The file can then be used to share information among processes. This practice is dangerous because files in a shared directory can be easily hijacked or manipulated by an attacker. Mitigation strategies include the following:
2613
26141. Use other low-level inter-process communication (IPC) mechanisms, such as sockets or shared memory.
26152. Use higher-level IPC mechanisms, such as remote procedure call (RPC).
26163. Use secure directories that can be accessed only by a program itself (Avoid race conditions in the case of multiple threads or processes.)
2617
2618The following lists several methods for creating temporary files. Product teams can use one or more of these methods as required or customize their own methods.
2619
26201. Files must have appropriate permissions so that they can be accessed only by authorized users.
26212. The name of a created file is unique or unpredictable.
26223. Files can be created and opened only if the files do not exist (atomic create and open).
26234. Open the files with exclusive access to avoid race conditions.
26245. Remove files before the program exits.
2625
2626In addition, when two or more users or a group of users have read/write permission to a directory, the potential security risk of the shared directory is far greater than that of the access to temporary files in the directory.
2627
2628Creating temporary files in a shared directory is vulnerable. For example, code that works for a locally mounted file system may be vulnerable when shared with a remotely mounted file system. The secure solution is not to create temporary files in shared directories.
2629
2630**\[Noncompliant Code Example]**
2631
2632The program creates a temporary file with a hard-coded file name in the shared directory **/tmp**  to store temporary data. Because the file name is hard-coded and consequently predictable, an attacker only needs to replace the file with a symbolic link. The target file referenced by the link is then opened and new content can be written.
2633
2634```c
2635void ProcData(const char *filename)
2636{
2637    FILE *fp = fopen(filename, "wb+");
2638    if (fp == NULL) {
2639        ... // Error handling
2640    }
2641
2642    ... // Write a file.
2643
2644    fclose(fp);
2645}
2646
2647int main(void)
2648{
2649    // Noncompliant: 1. A temporary file is created in shared directories. 2. The temporary file name is hard-coded.
2650    char *pFile = "/tmp/data";
2651    ...
2652
2653    ProcData(pFile);
2654
2655    ...
2656    return 0;
2657}
2658```
2659
2660**\[Compliant Code Example]**
2661
2662```c
2663Do not create temporary files that are used only by a program itself in this directory.
2664```
2665
2666**\[Impact]**
2667
2668Creating temporary files in an insecure manner may cause unauthorized access to the files and privilege escalation in the local system.
2669
2670## Do not access shared objects in signal handlers
2671
2672**\[Description]**
2673
2674Accessing or modifying shared objects in signal handlers can result in race conditions that can leave data in an uncertain state. This rule is not applicable to the following scenarios (see paragraph 5 in section 5.1.2.3 of the C11 standard):
2675
2676- Read/write operations on lock-free atomic object
2677- Read/write operations on objects of the **volatile sig\_atomic\_t** type. An object of the **volatile sig\_atomic\_t** type can be accessed as an atomic entity even in the presence of asynchronous interrupts, and is asynchronous-safe.
2678
2679**\[Noncompliant Code Example]**
2680
2681In the signal handler, the program attempts to use `g_msg` as the shared object and update the shared object when the SIGINT signal is delivered. However, `g_msg` is not a variable of type `volatile sig_atomic_t`, and is not asynchronous-safe.
2682
2683```c
2684#define MAX_MSG_SIZE 32
2685static char g_msgBuf[MAX_MSG_SIZE] = {0};
2686static char *g_msg = g_msgBuf;
2687
2688void SignalHandler(int signum)
2689{
2690    // It is noncompliant to use g_msg because it is not asynchronous-safe.
2691    (void)memset(g_msg,0, MAX_MSG_SIZE);
2692    errno_t ret = strcpy(g_msg,  "signal SIGINT received.");
2693    ... // Process ret
2694}
2695
2696int main(void)
2697{
2698    errno_t ret = strcpy(g_msg,  "No msg yet."); // Initialize message content.
2699    ... // Process ret
2700
2701    signal(SIGINT, SignalHandler); // Set the SIGINT signal handler.
2702
2703    ... // Main code loop
2704
2705    return 0;
2706}
2707```
2708
2709**\[Compliant Code Example]**
2710
2711In the following code example, only the `volatile sig_atomic_t` type is used as a shared object in signal handlers.
2712
2713```c
2714#define MAX_MSG_SIZE 32
2715volatile sig_atomic_t g_sigFlag = 0;
2716
2717void SignalHandler(int signum)
2718{
2719    g_sigFlag = 1; // Compliant
2720}
2721
2722int main(void)
2723{
2724    signal(SIGINT, SignalHandler);
2725    char msgBuf[MAX_MSG_SIZE];
2726    errno_t ret = strcpy(msgBuf, "No msg yet."); // Initialize message content.
2727    ... // Process ret
2728
2729    ... // Main code loop
2730
2731    if (g_sigFlag == 1) {  // Update message content based on g_sigFlag status after exiting the main loop.
2732        ret = strcpy(msgBuf,  "signal SIGINT received.");
2733        ... // Process ret
2734    }
2735
2736    return 0;
2737}
2738```
2739
2740**\[Impact]**
2741
2742Accessing or modifying shared objects in signal handlers may cause inconsistent status access data.
2743
2744## Do not use rand() to generate pseudorandom numbers for security purposes
2745
2746**\[Description]**
2747
2748The **rand()** function in the C language standard library generates pseudorandom numbers, which does not ensure the quality of the random sequence produced. In the C11 standard, the range of random numbers generated by the **rand()** function is `[0, RAND_MAX(0x7FFF)]`, which has a relatively short cycle, and the numbers can be predictable. Therefore, do not use the random numbers generated by the **rand()** function for security purposes. Use secure random number generation methods.
2749
2750Typical scenarios for security purposes include but are not limited to the following:
2751
2752- Generation of session IDs;
2753- Generation of random numbers in the challenge algorithm;
2754- Generation of random numbers of verification codes;
2755- Generation of random numbers for cryptographic algorithm purposes (for example, generating IVs, salt values, and keys).
2756
2757**\[Noncompliant Code Example]**
2758
2759The programmer wants the code to generate a unique HTTP session ID that is not predictable. However, the ID is a random number produced by calling the **rand()** function, and is predictable and has limited randomness.
2760
2761**\[Impact]**
2762
2763Using the **rand()** function may result in random numbers that are predictable.
2764
2765## Do not output the address of an object or function in a released version
2766
2767**\[Description]**
2768
2769Do not output the address of an object or function in a released version. For example, do not output the address of a variable or function to a client, log, or serial port.
2770
2771Before launching an advanced attack, the attacker usually needs to obtain the memory address (such as the variable address and function address) of the target program and then modify the content of the specified memory for attacks. If the program itself outputs the addresses of objects or functions, the attacker can take this advantage and use these addresses and offsets to calculate the addresses of other objects or functions and launch attacks. In addition, the protection function of address space randomization also fails due to memory address leakage.
2772
2773**\[Noncompliant Code Example]**
2774
2775In the following code example, the address to which the pointer points is logged in the %p format.
2776
2777```c
2778int Encode(unsigned char *in, size_t inSize, unsigned char *out, size_t maxSize)
2779{
2780    ...
2781    Log("in=%p, in size=%zu, out=%p, max size=%zu\n", in, inSize, out, maxSize);
2782    ...
2783}
2784```
2785
2786Note: This example uses only the %p format for logging pointers. In scenarios where pointers are converted to integers and then logged, the same risk exists.
2787
2788**\[Compliant Code Example]**
2789
2790In the following code example, the code for logging the address is deleted.
2791
2792```c
2793int Encode(unsigned char *in, size_t inSize, unsigned char *out, size_t maxSize)
2794{
2795    ...
2796    Log("in size=%zu, max size=%zu\n", inSize, maxSize);
2797    ...
2798}
2799```
2800
2801**\[Exception]**
2802
2803When the program crashes and exits, the memory address and other information can be output in the crash exception information.
2804
2805**\[Impact]**
2806
2807Memory address leakage creates vulnerabilities to adversaries, probably leading to an address space randomization protection failure.
2808
2809## Do not include public IP addresses in code
2810
2811**\[Description]**
2812
2813If the public IP addresses that are invisible and unknown to users are included in code or scripts, customers may doubt code security.
2814
2815Public network addresses (including public IP addresses, public URLs/domain names, and email addresses) contained in the released software (including software packages and patch packages) must meet the following requirements: 1\. Do not contain any public network address that is invisible on UIs or not disclosed in product documentation. 2\. Do not write disclosed public IP addresses in code or scripts. They can be stored in configuration files or databases.
2816
2817The public IP addresses built in open-source or third-party software must meet the first requirement at least.
2818
2819**\[Exception]**
2820
2821This requirement is not mandatory when public network addresses must be specified as required by standard protocols. For example, an assembled public network URL must be specified for the namespace of functions based on the SOAP protocol. W3.org addresses on HTTP pages are also exceptions.
2822
2823# Secure Kernel Coding
2824
2825## Ensure that the mapping start address and space size in kernel mmap are validated
2826
2827**\[Description]**
2828
2829In the mmap interface of the  kernel, the **remap\_pfn\_range()** function is often used to map the physical memory of a device to a user process space. If the parameters (such as the mapping start address) are controlled by the user mode and no validation is performed, the user mode can read and write any kernel address through the mapping. An attacker can even construct arguments to run arbitrary code in the kernel.
2830
2831**\[Noncompliant Code Example]**
2832
2833When **remap\_pfn\_range()** is used for memory mapping in the following code, the user-controllable mapping start address and space size are not validated. As a result, the kernel may crash or any code may be executed.
2834
2835```c
2836static int incorrect_mmap(struct file *file, struct vm_area_struct *vma)
2837{
2838	unsigned long size;
2839	size = vma->vm_end - vma->vm_start;
2840	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
2841	// Error: The mapping start address and space size are not validated.
2842	if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, size, vma->vm_page_prot)) {
2843		err_log("%s, remap_pfn_range fail", __func__);
2844		return EFAULT;
2845	} else {
2846		vma->vm_flags &=  ~VM_IO;
2847	}
2848
2849	return EOK;
2850}
2851```
2852
2853**\[Compliant Code Example]**
2854
2855Add the validity check on parameters such as the mapping start address.
2856
2857```c
2858static int correct_mmap(struct file *file, struct vm_area_struct *vma)
2859{
2860	unsigned long size;
2861	size = vma->vm_end - vma->vm_start;
2862	// Modification: Add a function to check whether the mapping start address and space size are valid.
2863	if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) {
2864		return EINVAL;
2865	}
2866
2867	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
2868	if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, size, vma->vm_page_prot)) {
2869		err_log( "%s, remap_pfn_range fail ", __func__);
2870		return EFAULT;
2871	} else {
2872		vma->vm_flags &=  ~VM_IO;
2873	}
2874
2875	return EOK;
2876}
2877```
2878
2879## Kernel programs must use kernel-specific functions to read and write user-mode buffers
2880
2881**\[Description]**
2882
2883During data exchange between the user mode and kernel mode, if no check (such as address range check and null pointer check) is performed in the kernel and the user mode input pointer is directly referenced, the kernel may crash and any address may be read or written when an invalid pointer is input in the user mode. Therefore, do not use unsafe functions such as **memcpy()** and **sprintf()**. Instead, use the dedicated functions provided by the kernel, such as **copy\_from\_user()**, **copy\_to\_user()**, **put\_user()**, and **get\_user()**, to read and write the user-mode buffer. Input validation is added to these functions.
2884
2885The forbidden functions are **memcpy()**, **bcopy()**, **memmove()**, **strcpy()**, **strncpy()**, **strcat()**, **strncat()**, **sprintf()**, **vsprintf()**, **snprintf()**, **vsnprintf()**, **sscanf()** and **vsscanf()**.
2886
2887**\[Noncompliant Code Example]**
2888
2889The kernel mode directly uses the buf pointer input by the user mode as the argument of **snprintf()**. When **buf** is **NULL**, the kernel may crash.
2890
2891```c
2892ssize_t incorrect_show(struct file *file, char__user *buf, size_t size, loff_t *data)
2893{
2894	// Error: The user-mode pointer is directly referenced. If the value of buf is NULL, a null pointer causes kernel crashes.
2895	return snprintf(buf, size, "%ld\n", debug_level);
2896}
2897```
2898
2899**\[Compliant Code Example]**
2900
2901Use the **copy\_to\_user()** function instead of **snprintf()**.
2902
2903```c
2904ssize_t correct_show(struct file *file, char __user *buf, size_t size, loff_t *data)
2905{
2906	int ret = 0;
2907	char level_str[MAX_STR_LEN] = {0};
2908	snprintf(level_str, MAX_STR_LEN, "%ld \n", debug_level);
2909	if(strlen(level_str) >= size) {
2910		return EFAULT;
2911	}
2912
2913	// Modification: Use the dedicated function copy_to_user() to write data to the user-mode buf and prevent buffer overflow.
2914	ret = copy_to_user(buf, level_str, strlen(level_str)+1);
2915	return ret;
2916}
2917```
2918
2919**\[Noncompliant Code Example]**
2920
2921The pointer **user\_buf** transferred in user mode is used as the data source to perform the **memcpy()** operation in kernel mode. When **user\_buf** is **NULL**, the kernel may crash.
2922
2923```c
2924size_t incorrect_write(struct file  *file, const char __user  *user_buf, size_t count, loff_t  *ppos)
2925{
2926	...
2927	char buf [128] = {0};
2928	int buf_size = 0;
2929	buf_size = min(count, (sizeof(buf)-1));
2930	// Error: The user-mode pointer is directly referenced. If user_buf is NULL, the kernel may crash.
2931	(void)memcpy(buf, user_buf, buf_size);
2932	...
2933}
2934```
2935
2936**\[Compliant Code Example]**
2937
2938Replace **memcpy()** with **copy\_from\_user()**.
2939
2940```c
2941ssize_t correct_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos)
2942{
2943	...
2944	char buf[128] = {0};
2945	int buf_size = 0;
2946
2947	buf_size = min(count, (sizeof(buf)-1));
2948	// Modification: Use the dedicated function copy_from_user() to write data to the kernel-mode buf and prevent buffer overflows.
2949	if (copy_from_user(buf, user_buf, buf_size)) {
2950		return EFAULT;
2951	}
2952
2953	...
2954}
2955```
2956
2957## Verify the copy length of **copy\_from\_user()** to prevent buffer overflows
2958
2959**\[Description]**
2960
2961The **copy\_from\_user()** function is used in kernel mode to copy data from the user mode. If the length of the copied data is not validated or is improperly validated, the kernel buffer overflows, causing kernel panic or privilege escalation.
2962
2963**\[Noncompliant Code Example]**
2964
2965The copy length is not validated.
2966
2967```c
2968static long gser_ioctl(struct file  *fp, unsigned cmd, unsigned long arg)
2969{
2970	char smd_write_buf[GSERIAL_BUF_LEN];
2971	switch (cmd)
2972	{
2973		case GSERIAL_SMD_WRITE:
2974			if (copy_from_user(&smd_write_arg, argp, sizeof(smd_write_arg))) {...}
2975			// Error: The value of smd_write_arg.size is entered by the user and is not validated.
2976			copy_from_user(smd_write_buf, smd_write_arg.buf, smd_write_arg.size);
2977			...
2978	}
2979}
2980```
2981
2982**\[Compliant Code Example]**
2983
2984Length validation is added.
2985
2986```c
2987static long gser_ioctl(struct file *fp, unsigned cmd, unsigned long arg)
2988{
2989	char smd_write_buf[GSERIAL_BUF_LEN];
2990	switch (cmd)
2991	{
2992		case GSERIAL_SMD_WRITE:
2993			if (copy_from_user(&smd_write_arg, argp, sizeof(smd_write_arg))){...}
2994			// Modification: Add validation.
2995			if (smd_write_arg.size  >= GSERIAL_BUF_LEN) {......}
2996			copy_from_user(smd_write_buf, smd_write_arg.buf, smd_write_arg.size);
2997 			...
2998	}
2999}
3000```
3001
3002## Initialize the data copied by **copy\_to\_user()** to prevent information leakage
3003
3004**\[Description]**
3005
3006**Note:** When **copy\_to\_user()** is used in kernel mode to copy data to the user mode, sensitive information (such as the pointer on the stack) may be leaked if the data is not completely initialized (for example, the structure member is not assigned a value, or the memory fragmentation is caused by byte alignment). Attackers can bypass security mechanisms such as Kaslr.
3007
3008**\[Noncompliant Code Example]**
3009
3010The data structure members are not completely initialized.
3011
3012```c
3013static long rmnet_ctrl_ioctl(struct file *fp, unsigned cmd, unsigned long arg)
3014{
3015	struct ep_info info;
3016	switch (cmd) {
3017		case FRMNET_CTRL_EP_LOOKUP:
3018			info.ph_ep_info.ep_type = DATA_EP_TYPE_HSUSB;
3019			info.ipa_ep_pair.cons_pipe_num = port->ipa_cons_idx;
3020			info.ipa_ep_pair.prod_pipe_num = port->ipa_prod_idx;
3021			// Error: The info structure has four members, not all of which are assigned with values.
3022			ret = copy_to_user((void __user *)arg, &info, sizeof(info));
3023			...
3024	}
3025}
3026```
3027
3028**\[Compliant Code Example]**
3029
3030Initialize all data.
3031
3032```c
3033static long rmnet_ctrl_ioctl(struct file *fp, unsigned cmd, unsigned long arg)
3034{
3035	struct ep_info info;
3036	// Modification: Use memset to initialize the buffer to ensure that no memory fragmentation exists due to byte alignment or no value assignment.
3037	(void)memset(&info, '0', sizeof(ep_info));
3038	switch (cmd) {
3039		case FRMNET_CTRL_EP_LOOKUP:
3040			info.ph_ep_info.ep_type = DATA_EP_TYPE_HSUSB;
3041			info.ipa_ep_pair.cons_pipe_num = port->ipa_cons_idx;
3042			info.ipa_ep_pair.prod_pipe_num = port->ipa_prod_idx;
3043			ret = copy_to_user((void __user *)arg, &info, sizeof(info));
3044			...
3045	}
3046}
3047```
3048
3049## Do not use the BUG\_ON macro in exception handling to avoid kernel panic
3050
3051**\[Description]**
3052
3053The BUG\_ON macro calls the **panic()** function of the kernel to print error information and suspend the system. In normal logic processing (for example, the **cmd** parameter of the **ioctl** interface cannot be identified), the system should not crash. Do not use the BUG\_ON macro in such exception handling scenarios. The WARN\_ON macro is recommended.
3054
3055**\[Noncompliant Code Example]**
3056
3057The BUG\_ON macro is used in the normal process.
3058
3059```c
3060/ * Determine whether the timer on the Q6 side is busy. 1: busy; 0: not busy */
3061static unsigned int is_modem_set_timer_busy(special_timer *smem_ptr)
3062{
3063	int i = 0;
3064	if (smem_ptr == NULL) {
3065		printk(KERN_EMERG"%s:smem_ptr NULL!\n", __FUNCTION__);
3066		// Error: The system BUG_ON macro calls panic() after printing the call stack, which causes kernel DoS and should not be used in normal processes.
3067		BUG_ON(1);
3068		return 1;
3069	}
3070
3071	...
3072}
3073```
3074
3075**\[Compliant Code Example]**
3076
3077Remove the BUG\_ON macro.
3078
3079```c
3080/ * Determine whether the timer on the Q6 side is busy. 1: busy; 0: not busy */
3081static unsigned int is_modem_set_timer_busy(special_timer *smem_ptr)
3082{
3083	int i = 0;
3084	if (smem_ptr == NULL) {
3085		printk(KERN_EMERG"%s:smem_ptr NULL!\n",  __FUNCTION__);
3086		// Modification: Remove the BUG_ON call or use WARN_ON.
3087		return 1;
3088	}
3089
3090	...
3091}
3092```
3093
3094## Do not use functions that may cause the process hibernation in the interrupt handler or in the context code of the process that holds the spin lock
3095
3096**\[Description]**
3097
3098Processes as the scheduling unit. In the interrupt context, only the interrupt with a higher priority can be interrupted. The system cannot schedule processes during interrupt processing. If the interrupt handler is in hibernation state, the kernel cannot be woken up, paralyzing the kernel.
3099
3100Spin locks disable preemption. If the spin lock enters the hibernation state after being locked, other processes will stop running because they cannot obtain the CPU (single-core CPU). In this case, the system does not respond and is suspended.
3101
3102Therefore, functions that may cause hibernation (such as **vmalloc()** and **msleep()**), block (such as **copy\_from\_user()**, **copy\_to\_user()**), or consume a large amount of time (such as **printk()**) should not be used in the interrupt processing program or the context code of the process that holds the spin lock.
3103
3104## Use the kernel stack properly to prevent kernel stack overflows
3105
3106**\[Description]**
3107
3108The kernel stack size is fixed (8 KB for a 32-bit system and 16 KB for a 64-bit system). Improper use of the kernel stack may cause stack overflows and system suspension. Therefore, the following requirements must be met:
3109
3110- The memory space requested on the stack cannot exceed the size of the kernel stack.
3111- Pay attention to the number of function nestings.
3112- Do not define excessive variables.
3113
3114**\[Noncompliant Code Example]**
3115
3116The variables defined in the following code are too large, causing stack overflows.
3117
3118```c
3119...
3120struct result
3121{
3122	char name[4];
3123	unsigned int a;
3124	unsigned int b;
3125	unsigned int c;
3126	unsigned int d;
3127}; // The size of the result structure is 20 bytes.
3128
3129int foo()
3130{
3131	struct result temp[512];
3132	// Error: The temp array contains 512 elements. The total size is 10 KB, which is far greater than the kernel stack size.
3133	(void)memset(temp, 0, sizeof(result) * 512);
3134	... // use temp do something
3135	return 0;
3136}
3137
3138...
3139```
3140
3141The **temp** array has 512 elements, and the total size is 10 KB, which is far greater than the kernel size (8 KB). The stack overflows obviously.
3142
3143**\[Compliant Code Example]**
3144
3145Use **kmalloc()** instead.
3146
3147```c
3148...
3149struct result
3150{
3151	char name[4];
3152	unsigned int a;
3153	unsigned int b;
3154	unsigned int c;
3155	unsigned int d;
3156}; // The size of the result structure is 20 bytes.
3157
3158int foo()
3159{
3160	struct result  *temp = NULL;
3161	temp = (result *)kmalloc(sizeof(result) * 512, GFP_KERNEL); // Modification: Use kmalloc() to apply for memory.
3162	... // check temp is not NULL
3163	(void)memset(temp, 0, sizeof(result)  * 512);
3164	... // use temp do something
3165	... // free temp
3166	return 0;
3167}
3168...
3169```
3170
3171## Restore address validation after the operation is complete
3172
3173**\[Description]**
3174
3175The SMEP security mechanism prevents the kernel from executing the code in the user space (PXN is the SMEP of the ARM version). System calls (such as **open()** and **write()**) are originally provided for user space programs to access. By default, these functions validate the input address. If it is not a user space address, an error is reported. Therefore, disable address validation before using these system calls in a kernel program. **set\_fs()**/**get\_fs()** is used to address this problem. For details, see the following code:
3176
3177```c
3178...
3179mmegment_t old_fs;
3180printk("Hello, I'm the module that intends to write message to file.\n");
3181if (file == NULL) {
3182	file = filp_open(MY_FILE, O_RDWR | O_APPEND | O_CREAT, 0664);
3183}
3184
3185if (IS_ERR(file)) {
3186	printk("Error occurred while opening file %s, exiting ...\n", MY_FILE);
3187	return 0;
3188}
3189
3190sprintf(buf, "%s", "The Message.");
3191old_fs = get_fs(); // get_fs() is used to obtain the upper limit of the user space address.
3192                   // #define get_fs() (current->addr_limit
3193set_fs(KERNEL_DS); // set_fs is used to increase the upper limit of the address space to KERNEL_DS so that the kernel code can call system functions.
3194file->f_op->write(file, (char *)buf, sizeof(buf), &file->f_pos); // The kernel code can call the write() function.
3195set_fs(old_fs); // Restore the address limit of the user space in time after use.
3196...
3197```
3198
3199According to the preceding code, it is vital to restore address validation immediately after the operation is complete. Otherwise, the SMEP/PXN security mechanism will fail, making it easy to exploit many vulnerabilities.
3200
3201**\[Noncompliant Code Example]**
3202
3203The program error processing branch does not use **set\_fs()** to restore address validation.
3204
3205```c
3206...
3207oldfs = get_fs();
3208set_fs(KERNEL_DS);
3209/* Create a done file in the timestamp directory. */
3210fd = sys_open(path, O_CREAT | O_WRONLY, FILE_LIMIT);
3211if (fd < 0) {
3212	BB_PRINT_ERR("sys_mkdir[%s] error, fd is[%d]\n", path, fd);
3213	return; // Error: Address validation is not restored in the error processing program branch.
3214}
3215
3216sys_close(fd);
3217set_fs(oldfs);
3218...
3219```
3220
3221**\[Compliant Code Example]**
3222
3223Address validation is restored in the error processing program.
3224
3225```c
3226...
3227oldfs = get_fs();
3228set_fs(KERNEL_DS);
3229
3230/* Create a done file in the timestamp directory. */
3231fd = sys_open(path, O_CREAT | O_WRONLY, FILE_LIMIT);
3232if (fd < 0) {
3233	BB_PRINT_ERR("sys_mkdir[%s] error, fd is[%d] \n", path, fd);
3234	set_fs(oldfs); // Modification: Restore address validation in the error processing program branch.
3235	return;
3236}
3237
3238sys_close(fd);
3239set_fs(oldfs);
3240...
3241```