The majority of the standard library functions, including I/O functions and memory allocation functions, return either a valid value or a value of the correct return type that indicates an error (for example, −1 or a null pointer). Assuming that all calls to such functions will succeed and failing to check the return value for an indication of an error is a dangerous practice that may lead to unexpected or undefined behavior when an error occurs. It is essential that programs detect and appropriately handle all errors in accordance with an error-handling policy.
The successful completion or failure of each of the standard library functions listed in the following table shall be determined either by comparing the function’s return value with the value listed in the column labeled “Error Return” or by calling one of the library functions mentioned in the footnotes.
Standard Library Functions
Function | Successful Return | Error Return |
---|---|---|
| Pointer to space |
|
|
| Nonzero |
|
| Nonzero |
|
| Nonzero |
| Pointer to matching element |
|
| Pointer to matching element |
|
| Converted wide character |
|
| Number of bytes |
|
| Number of bytes |
|
| Pointer to space |
|
| Processor time |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Nonzero |
|
|
|
|
|
|
| Character read |
|
|
| Nonzero, |
| Pointer to string |
|
| Wide character read |
|
| Pointer to stream |
|
|
| Nonzero |
| Number of characters (nonnegative) | Negative |
| Number of characters (nonnegative) | Negative |
| Character written |
|
| Nonnegative |
|
fputwc() | Wide character written | WEOF |
| Nonnegative |
|
| Elements read | Elements read |
| Pointer to stream |
|
|
| Nonzero |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
|
| Nonzero |
|
| Nonzero, |
| File position |
|
| Number of wide characters (nonnegative) | Negative |
| Number of wide characters (nonnegative) | Negative |
| Elements written | Elements written |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Character read |
|
| Character read |
|
| Pointer to string |
|
| Pointer to string |
|
| Pointer to string |
|
| Wide character read |
|
| Wide character read |
|
| Pointer to broken-down time |
|
| Pointer to broken-down time |
|
| Pointer to broken-down time |
|
| Pointer to broken-down time |
|
| Pointer to space |
|
| Number of bytes |
|
| Number of bytes or status |
|
| Number of bytes or status |
|
| Number of bytes or status |
|
| Number of bytes or status |
|
| Number of non-null elements |
|
|
| Nonzero |
| Number of non-null elements |
|
|
| Nonzero |
| Number of bytes |
|
| Pointer to located character |
|
| Calendar time |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Number of characters (nonnegative) | Negative |
| Character written |
|
| Wide character written |
|
|
| Nonzero |
| Pointer to space |
|
|
| Nonzero |
|
| Nonzero |
| Pointer to string |
|
|
| Nonzero |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Pointer to previous function |
|
| Number of characters that would be written (nonnegative) | Negative |
| Number of characters that would be written (nonnegative) | Negative |
| Number of non-null characters written | Negative |
| Number of non-null characters written | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Pointer to located character |
|
|
| Nonzero |
| Number of non-null characters |
|
| Pointer to located character |
|
| Pointer to located character |
|
| Pointer to located string |
|
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Pointer to first character of a token |
|
| Pointer to first character of a token |
|
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Length of transformed string |
|
| Number of non-null wide characters | Negative |
| Number of non-null wide characters | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
|
|
|
|
|
|
|
|
|
|
| Negative |
| Calendar time |
|
| Base |
|
| Pointer to stream |
|
|
| Nonzero |
| Non-null pointer |
|
|
| Nonzero |
|
|
|
| Value of thread-specific storage |
|
|
|
|
| Character pushed back |
|
| Character pushed back |
|
| Number of characters (nonnegative) | Negative |
| Number of characters (nonnegative) | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Number of wide characters (nonnegative) | Negative |
| Number of wide characters (nonnegative) | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Number of characters (nonnegative) | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Number of characters that would be written (nonnegative) | Negative |
| Number of characters that would be written (nonnegative) | Negative |
| Number of non-null characters (nonnegative) | Negative |
| Number of non-null characters (nonnegative) | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Number of non-null wide characters | Negative |
| Number of non-null wide characters | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Number of wide characters (nonnegative) | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Number of bytes stored |
|
| Pointer to located wide character |
|
| Number of non-null wide characters |
|
| Pointer to located wide character |
|
| Pointer to located wide character |
|
| Number of non-null bytes |
|
|
| Nonzero |
| Pointer to located wide string |
|
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Pointer to first wide character of a token |
|
| Pointer to first wide character of a token |
|
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Number of non-null bytes |
|
|
| Nonzero |
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Length of transformed wide string |
|
| Converted character |
|
| Number of bytes stored |
|
| Number of bytes stored |
|
| Valid argument to |
|
| Valid argument to |
|
| Pointer to located wide character |
|
| Number of wide characters (nonnegative) | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
Note: According to FIO35-C. Use feof() and ferror() to detect end-of-file and file errors when sizeof(int) == sizeof(char), callers should verify end-of-file and file errors for the functions in this table as follows:
1 By calling ferror()
and feof()
2 By calling ferror()
The ungetc()
function does not set the error indicator even when it fails, so it is not possible to check for errors reliably unless it is known that the argument is not equal to EOF
.
The C Standard 7.31.3.10 paragraph 3 [ISO/IEC 9899:2024] states that
)ne wide character of pushback is guaranteed...
so this should not be an issue if, at most, one character is ever pushed back before reading again. (See FIO13-C. Never push back anything other than one read character.)
Noncompliant Code Example (setlocale()
)
In this noncompliant code example, the function utf8_to_wcs()
attempts to convert a sequence of UTF-8 characters to wide characters. It first invokes setlocale()
to set the global locale to the implementation-defined en_US.UTF-8
but does not check for failure. The setlocale()
function will fail by returning a null pointer, for example, when the locale is not installed. The function may fail for other reasons as well, such as the lack of resources. Depending on the sequence of characters pointed to by utf8
, the subsequent call to mbstowcs()
may fail or result in the function storing an unexpected sequence of wide characters in the supplied buffer wcs
.
#include <locale.h> #include <stdlib.h> int utf8_to_wcs(wchar_t *wcs, size_t n, const char *utf8, size_t *size) { if (NULL == size) { return -1; } setlocale(LC_CTYPE, "en_US.UTF-8"); *size = mbstowcs(wcs, utf8, n); return 0; }
Compliant Solution (setlocale()
)
This compliant solution checks the value returned by setlocale()
and avoids calling mbstowcs()
if the function fails. The function also takes care to restore the locale to its initial setting before returning control to the caller.
#include <locale.h> #include <stdlib.h> int utf8_to_wcs(wchar_t *wcs, size_t n, const char *utf8, size_t *size) { if (NULL == size) { return -1; } const char *save = setlocale(LC_CTYPE, "en_US.UTF-8"); if (NULL == save) { return -1; } *size = mbstowcs(wcs, utf8, n); if (NULL == setlocale(LC_CTYPE, save)) { return -1; } return 0; }
Noncompliant Code Example (calloc()
)
In this noncompliant code example, temp_num
, tmp2
, and num_of_records
are derived from a tainted source. Consequently, an attacker can easily cause calloc()
to fail by providing a large value for num_of_records
.
#include <stdlib.h> #include <string.h> enum { SIG_DESC_SIZE = 32 }; typedef struct { char sig_desc[SIG_DESC_SIZE]; } signal_info; void func(size_t num_of_records, size_t temp_num, const char *tmp2, size_t tmp2_size_bytes) { signal_info *start = (signal_info *)calloc(num_of_records, sizeof(signal_info)); if (tmp2 == NULL) { /* Handle error */ } else if (temp_num > num_of_records || temp_num == 0) { /* Handle error */ } else if (tmp2_size_bytes < SIG_DESC_SIZE) { /* Handle error */ } signal_info *point = start + temp_num - 1; memcpy(point->sig_desc, tmp2, SIG_DESC_SIZE); point->sig_desc[SIG_DESC_SIZE - 1] = '\0'; /* ... */ free(start); }
When calloc()
fails, it returns a null pointer that is assigned to start
. If start
is null, an attacker can provide a value for temp_num
that, when scaled by sizeof(signal_info)
, references a writable address to which control is eventually transferred. The contents of the string referenced by tmp2
can then be used to overwrite the address, resulting in an arbitrary code execution vulnerability.
Compliant Solution (calloc()
)
To correct this error, ensure the pointer returned by calloc()
is not null:
#include <stdlib.h> #include <string.h> enum { SIG_DESC_SIZE = 32 }; typedef struct { char sig_desc[SIG_DESC_SIZE]; } signal_info; void func(size_t num_of_records, size_t temp_num, const char *tmp2, size_t tmp2_size_bytes) { signal_info *start = (signal_info *)calloc(num_of_records, sizeof(signal_info)); if (start == NULL) { /* Handle allocation error */ } else if (tmp2 == NULL) { /* Handle error */ } else if (temp_num > num_of_records || temp_num == 0) { /* Handle error */ } else if (tmp2_size_bytes < SIG_DESC_SIZE) { /* Handle error */ } signal_info *point = start + temp_num - 1; memcpy(point->sig_desc, tmp2, SIG_DESC_SIZE); point->sig_desc[SIG_DESC_SIZE - 1] = '\0'; /* ... */ free(start); }
Noncompliant Code Example (realloc()
)
This noncompliant code example calls realloc()
to resize the memory referred to by p
. However, if realloc()
fails, it returns a null pointer and the connection between the original block of memory and p
is lost, resulting in a memory leak.
#include <stdlib.h> void *p; void func(size_t new_size) { if (new_size == 0) { /* Handle error */ } p = realloc(p, new_size); if (p == NULL) { /* Handle error */ } }
This code example complies with MEM04-C. Do not perform zero-length allocations.
Compliant Solution (realloc()
)
In this compliant solution, the result of realloc()
is assigned to the temporary pointer q
and validated before it is assigned to the original pointer p
:
#include <stdlib.h> void *p; void func(size_t new_size) { void *q; if (new_size == 0) { /* Handle error */ } q = realloc(p, new_size); if (q == NULL) { /* Handle error */ } else { p = q; } }
Noncompliant Code Example (fseek()
)
In this noncompliant code example, the fseek()
function is used to set the file position to a location offset
in the file referred to by file
prior to reading a sequence of bytes from the file. However, if an I/O error occurs during the seek operation, the subsequent read will fill the buffer with the wrong contents.
#include <stdio.h> size_t read_at(FILE *file, long offset, void *buf, size_t nbytes) { fseek(file, offset, SEEK_SET); return fread(buf, 1, nbytes, file); }
Compliant Solution (fseek()
)
According to the C Standard, the fseek()
function returns a nonzero value to indicate that an error occurred. This compliant solution tests for this condition before reading from a file to eliminate the chance of operating on the wrong portion of the file if fseek()
fails:
#include <stdio.h> size_t read_at(FILE *file, long offset, void *buf, size_t nbytes) { if (fseek(file, offset, SEEK_SET) != 0) { /* Indicate error to caller */ return 0; } return fread(buf, 1, nbytes, file); }
Noncompliant Code Example (snprintf()
)
In this noncompliant code example, snprintf()
is assumed to succeed. However, if the call fails (for example, because of insufficient memory, as described in GNU libc bug 441945), the subsequent call to log_message()
has undefined behavior because the character buffer is uninitialized and need not be null-terminated.
#include <stdio.h> extern void log_message(const char *); void f(int i, int width, int prec) { char buf[40]; snprintf(buf, sizeof(buf), "i = %*.*i", width, prec, i); log_message(buf); /* ... */ }
Compliant Solution (snprintf()
)
This compliant solution does not assume that snprintf()
will succeed regardless of its arguments. It tests the return value of snprintf()
before subsequently using the formatted buffer. This compliant solution also treats the case where the static buffer is not large enough for snprintf()
to append the terminating null character as an error.
#include <stdio.h> #include <string.h> extern void log_message(const char *); void f(int i, int width, int prec) { char buf[40]; int n; n = snprintf(buf, sizeof(buf), "i = %*.*i", width, prec, i); if (n < 0 || n >= sizeof(buf)) { /* Handle snprintf() error */ strcpy(buf, "unknown error"); } log_message(buf); }
Compliant Solution (snprintf(
null)
)
If unknown, the length of the formatted string can be discovered by invoking snprintf()
with a null buffer pointer to determine the size required for the output, then dynamically allocating a buffer of sufficient size, and finally calling snprintf()
again to format the output into the dynamically allocated buffer. Even with this approach, the success of all calls still needs to be tested, and any errors must be appropriately handled. A possible optimization is to first attempt to format the string into a reasonably small buffer allocated on the stack and, only when the buffer turns out to be too small, dynamically allocate one of a sufficient size:
#include <stdio.h> #include <stdlib.h> #include <string.h> extern void log_message(const char *); void f(int i, int width, int prec) { char buffer[20]; char *buf = buffer; int n = sizeof(buffer); const char fmt[] = "i = %*.*i"; n = snprintf(buf, n, fmt, width, prec, i); if (n < 0) { /* Handle snprintf() error */ strcpy(buffer, "unknown error"); goto write_log; } if (n < sizeof(buffer)) { goto write_log; } buf = (char *)malloc(n + 1); if (NULL == buf) { /* Handle malloc() error */ strcpy(buffer, "unknown error"); goto write_log; } n = snprintf(buf, n, fmt, width, prec, i); if (n < 0) { /* Handle snprintf() error */ strcpy(buffer, "unknown error"); } write_log: log_message(buf); if (buf != buffer) { free(buf); } }
This solution uses the goto
statement, as suggested in MEM12-C. Consider using a goto chain when leaving a function on error when using and releasing resources.
Exceptions
ERR33-C-EX1: It is acceptable to ignore the return value of a function if:
- that function cannot fail.
- its return value is inconsequential; that is, it does not indicate an error.
- it is one of a handful of functions whose return values are not traditionally checked.
These functions are listed in the following table:
Functions for which Return Values Need Not Be Checked
Function | Successful Return | Error Return |
---|---|---|
| Character written |
|
| Wide character written |
|
| Nonnegative |
|
| Nonnegative |
|
| Number of characters (nonnegative) | Negative |
| Number of wide characters (nonnegative) | Negative |
kill_dependency() | The input parameter | NA |
memcpy() , wmemcpy() | The destination input parameter | NA |
memmove() , wmemmove() | The destination input parameter | NA |
strcpy() , wcscpy() | The destination input parameter | NA |
strncpy() , wcsncpy() | The destination input parameter | NA |
strcat() , wcscat() | The destination input parameter | NA |
strncat() , wcsncat() | The destination input parameter | NA |
memset() , wmemset() | The destination input parameter | NA |
The return value of a call to fprintf()
or one of its variants (vfprintf()
, wfprintf()
, vwfprintf()
) or one of the file output functions fputc()
, fputwc()
, fputs()
, fputws()
may be ignored if the output is being directed to stdout
or stderr
. Otherwise, the return value must be checked.
If a function's return value is to be ignored, it is recommended that the function's return value should be explicitly cast to void to signify the programmer's intent:
int main() { (void) fprintf(stdout, "Hello, world\n"); // fprintf() return value safely ignored }
Risk Assessment
Failing to detect error conditions can lead to unpredictable results, including abnormal program termination and denial-of-service attacks or, in some situations, could even allow an attacker to run arbitrary code.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
ERR33-C | High | Likely | Medium | P18 | L1 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Astrée | 24.04 | error-information-unused error-information-unused-computed | Partially checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-ERR33 | |
CodeSonar | 8.1p0 | LANG.FUNCS.IRV | Ignored return value Missing Test of Error Code Non-zero Error Code |
Compass/ROSE | Can detect violations of this recommendation when checking for violations of EXP12-C. Do not ignore values returned by functions and EXP34-C. Do not dereference null pointers | ||
Coverity | 2017.07 | MISRA C 2012 Rule 22.8 MISRA C 2012 Rule 22.9 MISRA C 2012 Rule 22.10 | Implemented |
Helix QAC | 2024.3 | C3200 C++3802, C++3803, C++3804 DF2820, DF2821, DF2822, DF2823, DF2824, DF2930, DF2931, DF2932, DF2933, DF2934 | |
Klocwork | 2024.3 | NPD.CHECK.MUST | |
LDRA tool suite | 9.7.1 | 80 D | Partially implemented |
Parasoft C/C++test | 2023.1 | CERT_C-ERR33-a | The value returned by a standard library function that may return an error should be used |
Parasoft Insure++ | Runtime analysis | ||
PC-lint Plus | 1.4 | 534 | Partially supported |
R2024a | Checks for:
Rule partially covered. | ||
RuleChecker | 24.04 | error-information-unused | Partially checked |
TrustInSoft Analyzer | 1.38 | pointer arithmetic | Exhaustively verified. |
Related Vulnerabilities
The vulnerability in Adobe Flash [VU#159523] arises because Flash neglects to check the return value from calloc()
. Even when calloc()
returns a null pointer, Flash writes to an offset from the return value. Dereferencing a null pointer usually results in a program crash, but dereferencing an offset from a null pointer allows an exploit to succeed without crashing the program.
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Key here (explains table format and definitions)
Taxonomy | Taxonomy item | Relationship |
---|---|---|
CERT C Secure Coding Standard | ERR00-C. Adopt and implement a consistent and comprehensive error-handling policy | Prior to 2018-01-12: CERT: Unspecified Relationship |
CERT C Secure Coding Standard | EXP34-C. Do not dereference null pointers | Prior to 2018-01-12: CERT: Unspecified Relationship |
CERT C Secure Coding Standard | FIO13-C. Never push back anything other than one read character | Prior to 2018-01-12: CERT: Unspecified Relationship |
CERT C Secure Coding Standard | MEM04-C. Do not perform zero-length allocations | Prior to 2018-01-12: CERT: Unspecified Relationship |
CERT C Secure Coding Standard | MEM12-C. Consider using a goto chain when leaving a function on error when using and releasing resources | Prior to 2018-01-12: CERT: Unspecified Relationship |
CERT C | ERR10-CPP. Check for error conditions | Prior to 2018-01-12: CERT: Unspecified Relationship |
CERT C | FIO04-CPP. Detect and handle input and output errors | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TS 17961:2013 | Failing to detect and handle standard library errors [liberr] | Prior to 2018-01-12: CERT: Unspecified Relationship |
CWE 2.11 | CWE-252, Unchecked Return Value | 2017-07-06: CERT: Partial overlap |
CWE 2.11 | CWE-253, Incorrect Check of Function Return Value | 2017-07-06: CERT: Partial overlap |
CWE 2.11 | CWE-391, Unchecked Error Condition | 2017-07-06: CERT: Rule subset of CWE |
CERT-CWE Mapping Notes
Key here for mapping notes
CWE-252/CWE-253/CWE-391 and ERR33-C/POS34-C
Independent( ERR33-C, POS54-C, FLP32-C, ERR34-C) Intersection( CWE-252, CWE-253) = Ø CWE-391 = Union( CWE-252, CWE-253) CWE-391 = Union( ERR33-C, POS34-C, list) where list =
- Ignoring return values of functions outside the C or POSIX standard libraries
Bibliography
[DHS 2006] | Handle All Errors Safely |
[Henricson 1997] | Recommendation 12.1, "Check for All Errors Reported from Functions" |
[ISO/IEC 9899:2024] | Subclause 7.31.3.10, "The ungetc Function" |
[VU#159523] |
44 Comments
Jussi Auvinen
The calloc() example has many problems, not only the not handled NULL pointer from the calloc().
Furthermore, the allocated memory is not freed or pointer to it is not conveyed outside of the function.
Aaron Ballman
Good catches! I've corrected the issues you've pointed out, thanks!
Markus Elfring
Will it be helpful to group information from the table “Standard Library Functions” by the item “Successful Return”?
(Can grouping of such data be nicer than the supported sorting?)
David Svoboda
All of our tables, including the table in this rule, allow you to sort their elements by whichever column header you click on.
Markus Elfring
Are there any chances that tree (or outline) views will become supported for such widgets?
David Svoboda
I have no idea. We are using Atlassian's Confluence software to run our wiki. You should ask them.
Markus Elfring
Have you got any better contacts to your wiki provider?
Markus Elfring
I hope that the section “Exceptions” can be reconsidered and accordingly improved.
Markus Elfring
How would you call a property (or category) of functions which forward an input parameter as their return value?
David Svoboda
In OOP, we typically call methods that return their argument chaining methods, because you can chain them together via their return values. But I don't know of such functions in the standard C library.
Markus Elfring
How does this feedback fit to documentation for functions like memcpy() and strcat()?
David Svoboda
It means that memcpy() & similar functions could be used as a chaining function. I feel like we are lost in the weeds here. Do you have a specific suggestion for improving this rule?
Markus Elfring
It seems that expressed knowledge limitations influence the communication in some ways.
Would you find a function property like “pass-through” also appropriate and helpful for further clarifications?
Yes, of course.
The corresponding change acceptance will take another while.
Markus Elfring
Under which circumstances would you care more for data output failures?
David Svoboda
It depends on what you mean by "data output failures", but the general answer is, if they introduce a significant new class of software vulnerabilities.
Markus Elfring
Will the development attention grow for more complete error detection and corresponding exception handling also according to function calls like the following?
David Svoboda
I think we've discussed these functions, and we are not going to mention that fputs() & co might return negative values besides EOF.
Markus Elfring
The development attention can eventually be influenced further by available information for the handling of success and error predicates.
Robert Schiela
Markus, do you have precise suggestions? I find these to be ambiguous and open ended and I'm finding it difficult to understand exactly what your suggestions are.
Markus Elfring
Yes, of course.
It seems to be hard to get the desired acceptance for some of the mentioned change possibilities.
I hoped that such a clarification approach can eventually trigger more constructive responses.
Would you like to take another look at items which we tried to discuss already?
Robert Schiela
Markus,
I meant, do you have precise technical suggestions? I do not interpret a "potential for collateral evolution" as a precise technical suggestion. I interpret it as a request for a different approach. Or a philosophical, theoretical, or strategic discussion. Not a transactional, actionable, precise change request to the Rules.
Examples:
These all seem like general statements or questions, and not clear, precise, actionable issues to consider. So, I'm not sure how to act on them or respond.
Markus Elfring
How actionable did you find suggestions like the following?
Robert Schiela
Markus,
I don't find these particularly actionable. They are open ended, and I don't know what entries or grouping you might be suggesting. What would help to be more actionable is something like:
As things stand right now, we think the lists are accurate and don't need any changes. We need to understand the very precise changes you think that should be made and very precise improvement value that change will have for us to fully consider your change request. Otherwise, we will not have enough information to properly consider your change request.
Markus Elfring
Can my change request from 2023-05-05 be understandable (in principle)?
Markus Elfring
An fputs() call is used within a source code example for a “compliant solution”.
Which special circumstances did influence the situation to keep file output functions in the table “Functions for which Return Values Need Not Be Checked” till May 2023?
Robert Schiela
I'm sorry, I simply don't understand your question.
Robert Schiela
Markus,
Note that the compliant solution you are referring to is on page EXP12-C. Do not ignore values returned by functions, which is a Recommendation, not a Rule. Please see Rules versus Recommendations for more information about the difference.
Markus Elfring
Reminder: You provided the following feedback on 2019-03-06.
“Though EXP12-C is a recommendation, and therefore it gets less priority for updates, we still don't want them to be inconsistent with the rules and exceptions of the rules. We're going to change the example so it is consistent.”
Robert Schiela
Markus,
I fail to recognize the inconsistency you are referring to, simply stating that fputs() is used in a Compliant Solution (CS) on that page. Can you precisely define what the inconsistency is in a straightforward "X is not consistent with Y" way for us to consider in a clear and actionable way?
In particular, if there is a CS that is not consistent with a Rule, we will consider that as a higher priority. By their nature, recommendations are not always applicable and are likely to have natural conflicts that may result in inconstancies, so we generally deem them as lesser priority, or simply not resolvable.
Markus Elfring
I wonder about such a feedback.
The information “This compliant solution checks to make sure no output error occurred (see ERR33-C…).” is provided.
Thus it seems that software developers get encouraged to take special care also for the return value of such a file output function.
Does the current table “Functions for which Return Values Need Not Be Checked” contain contradictions (in comparison to other programming advices)?
Robert Schiela
Markus,
I'm still struggling to understand the precise inconsistency you are referring to, and I don't understand the intent or meaning about what you might be "wonder[ing] about such a feedback."
The EXP12-C Recommendation does recommend that developers take extra care. It is a Recommendation that is above and beyond the Rule. It is a priority that our CS's do NOT violate Rules. We do not guarantee that our CS's comply with all Recommendations. Because EXP12-C is a Recommendation, some of our CS's may not comply with it, as they may not comply with other Recommendations, depending on the context. Additionally, the Exceptions EXP12-C-EX1 and EXP12-C-EX2 refer to the Exception in this rule ERR33-C-EX1. We interpret those Exceptions to be consistent.
We are not aware of the tables or guidance in this Rule contradicting anything else in this or any other Rules. If you find such a contradiction and can provide precise pointers or information that we are able to act on, we will consider it.
That said, we are considering making a change to the examples in EXP12-C, but I don't think it is for the reason you were trying to point out. EXP12-C says "Unlike this recommendation, [EXP33-C] is restricted to functions from the Standard C library." That implies that the significant difference between EXP12-C and EXP33-C is that EXP12-C is about return values for functions that are not from the Standard C library. However, the only example in EXP12-C uses fputs(), which is part of the Standard C library. So, the example using a function that is part of the Standard C library is inconsistent with the intent that EXP12-C is about functions not part of the Standard C library. We are considering changing the example to use a function that is not part of the Standard C library.
Markus Elfring
There are some communication factors involved.
Another example:
I pointed the following information out on 2019-02-14.
You gave the following information within a bigger feedback on 2019-03-06.
How interesting is it then that you seem to present other views today?
Our development views are different somehow also for this technical detail.
This view might fit only to your own improvable taxonomy.
How does information from the advice “CWE-252: Unchecked Return Value” fit into the discussed descriptions?
Robert Schiela
Markus,
This is an example of how more precision helps. If you are considering that comment from 2019, you also have to consider the page as it existed at the time of the comment. At that time, the example in EXP12-C was simply the puts() function. That was changed on 4/2/2019 by David. So, you are applying comments made from a version back then to the version as it is now, and the context has changed. This information and the previous versions are available in the page history. I interpret and my intent of my messaging now is that was a different precise inconsistency than we are talking about now. The difference in views is because it is a similar question but on a different piece of code and different context.
David Svoboda
I have changed the code examples in EXP12-C to use the asprintf() function. This function is part of the GNU C library, but has never been standardized in either ISO C or POSIX. So those code examples do not violate this rule or POS54-C. Detect and handle POSIX library errors.
Markus Elfring
David Svoboda
I would need a very strong argument to merge the table in ERR33-C-EX1 with the table in the introduction. I am currently against merging these tables. Both tables contain data straight from the standard, and which table a function goes into is pretty much our choice, based on the exception text. Merging these tables would require us to add an 'is this function in the exception' field, which is more complicated than their current state.
Markus Elfring
It seems that this feedback indicates another communication difficulty.
Which factors do hinder your organisation to integrate further change possibilities so far?
I am trying to adjust the information presentation in some ways for a while.
I proposed to move a few entries.
Which function categories will be left over for the description of known exceptions?
Markus Elfring
I do also not like the wording which you introduced yesterday.
I find the expressed return value ignorance too broad (for file output functions).
Under which circumstances will you get into the mood to take other possible adjustments into account?
Robert Schiela
Markus, can you be more specific about what you think is "too broad" about the update? It is limited to printing stdout and stderr, which are fairly precise constraints. Printing to stdout is effectively the same behavior as printf, which is already in the table of the exception. And printing to stderr is probably done as a way to notify of a prior error. If printing an error to stderr fails, what would be a reliable failback suggestion?
Markus Elfring
I find such a “restriction” generally questionable for the affected programming interface.
Please reconsider this view also according to the functionality “file redirection”.
This can usually be often the case.
Solutions should be known for such a recurring question.
Further hints and recommendations were published already for more advanced error reporting approaches elsewhere.
Robert Schiela
What is the question? I cannot tell if you think constraining the Exception to stdout and stderr is overly constraining, and the Exception should be broadened? Or if you think the Exception is too broad and overly constraining the Rule.
File redirection happens in the system outside of the program. Its behavior will be specified outside of the program. Often redirection fails will report errors directly to stderr, not as a return value back to the program that invoked stdout or stderr through the shell that redirected the stream to a file.
ERR00-C does not address how to report an error when error reporting itself occurs. That becomes a recursive paradox. We don't currently have much in the way of Recommendations regarding reporting program error status or return values on exit. ERR04-C discusses it a little. So, that could be a possible future improvement we can consider. But, based on our current understanding of your questions and concern, we would consider it a Recommendation improvement, not a Rule improvement. Note that ERR00-C and ERR04-C are both Recommendations, not Rules.
Markus Elfring
File output can occasionally fail here, can't it?
Would you like to reconsider such feedback if you would take test results better into account for the usage of a file like “/dev/full”?
Can any “Recommendations” eventually become promoted to “Rules” (or related items)?
samuel kellar
I believe there is an edge case for the CCE (calloc) which has not been addressed. Since
temp_num
derives from a "tainted source" (description in the non-compliant portion of the code example), it is possible the value is 0. I believe the result ofviolates ARR30-C. Do not form or use out-of-bounds pointers or array subscripts in that case.
David Svoboda
Yow! Good catch...I've fixed both code examples that use temp_num.