Skip to content

Fix potential buffer underflow when stripping trailing newlines - #1951

Open
AustinBenoit wants to merge 1 commit into
mainfrom
fix/newline-strip-buffer-underflow
Open

AustinBenoit wants to merge 1 commit into
mainfrom
fix/newline-strip-buffer-underflow

Conversation

@AustinBenoit

Copy link
Copy Markdown
Contributor

Description

Provide details of the change, and generalize the change in the PR title above.

Resolves GitHub Code Scanning security alerts (WS-CPP-00049):

  • #2788 (app/src/locale.cc:235)
  • #2789 (app/src/locale.cc:236)
  • #2791 (testing/run_all_tests.cc:97)

Both firebase::internal::GetTimezone() (app/src/locale.cc) and the Android native test runner (testing/run_all_tests.cc) read lines using fgets into a fixed buffer and stripped trailing newlines by indexing buf[strlen(buf) - 1]. If fgets reads a line starting with a NUL byte (\0), strlen returns 0 and len - 1 wraps around to SIZE_MAX (buf[-1]), resulting in a 1-byte buffer underflow. In testing/run_all_tests.cc, log[strlen(log) - 1] = '\0' also truncated the 255th character of any line longer than the buffer that did not end with \n.

This change:

  • Replaces raw C-array newline stripping (buf[strlen(buf) - 1] = '\0') in app/src/locale.cc and testing/run_all_tests.cc with std::string (if (!str.empty() && str.back() == '\n') { str.pop_back(); }).
  • Zero-initializes the stack buffers (char buf[kBufSize] = {0}; and char log[256] = {0};).
  • Closes tz_file (fclose(tz_file)) in GetTimezone() to fix a FILE* leak on Linux, and falls back to getenv("TZ") / tzname if /etc/timezone is blank after stripping the newline.
  • Replaces while (!feof(fp)) { if (fgets(...)) } in testing/run_all_tests.cc with while (fgets(log, sizeof(log), fp) != nullptr) to avoid an infinite loop if ferror(fp) is set.
  • Adds a release note entry under ### Upcoming in release_build_files/readme.md.

Testing

Describe how you've tested these changes. Link any manually triggered Integration tests or CPP binary SDK Packaging Github Action workflows, if applicable.

  • Compiled app/src/locale.cc on Linux desktop with g++ -std=c++17 -Wall -Wextra -Werror.
  • Verified formatting with python3 scripts/format_code.py -git_diff -noformat_file -verbose.
  • Verified copyright headers with bash scripts/gha/check_copyright.sh -g.

Type of Change

Place an x the applicable box:

  • Bug fix. Add the issue # below if applicable.
  • New feature. A non-breaking change which adds functionality.
  • Other, such as a build process or documentation change.

https://github.com/firebase/firebase-cpp-sdk/security/code-scanning/2788
https://github.com/firebase/firebase-cpp-sdk/security/code-scanning/2789
https://github.com/firebase/firebase-cpp-sdk/security/code-scanning/2791


Notes

  • Bug fixes and feature changes require an update to the Release Notes section of release_build_files/readme.md.
  • Read the contribution guidelines CONTRIBUTING.md.
  • Changes to the public API require an internal API review. If you'd like to help us make Firebase APIs better, please propose your change in a feature request so that we can discuss it together.

Use std::string::back() and std::string::pop_back() (with a non-empty check) instead of indexing buf[strlen(buf) - 1] in app/src/locale.cc and testing/run_all_tests.cc, and close /etc/timezone in GetTimezone().

Resolves GitHub Code Scanning security alerts (Rule WS-CPP-00049):
- https://github.com/firebase/firebase-cpp-sdk/security/code-scanning/2788
- https://github.com/firebase/firebase-cpp-sdk/security/code-scanning/2789
- https://github.com/firebase/firebase-cpp-sdk/security/code-scanning/2791

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request fixes a potential 1-byte buffer underflow and a file descriptor leak when reading /etc/timezone in GetTimezone(), and refactors the test log printing loop to safely handle newlines. The review feedback suggests handling CRLF line endings when parsing /etc/timezone by stripping both \r and \n characters, and optimizing the test logging loop by manipulating the raw C-string buffer directly to avoid the overhead of creating a std::string on each iteration.

Comment thread app/src/locale.cc
Comment on lines +236 to 242
std::string tz(buf);
if (!tz.empty() && tz.back() == '\n') {
tz.pop_back();
}
if (!tz.empty()) {
return tz;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If /etc/timezone contains CRLF (\r\n) line endings (e.g., if edited on Windows or shared across environments), only stripping \n will leave a trailing \r in the timezone string. This can cause issues when resolving the timezone. It is safer to strip both trailing \r and \n characters.

      std::string tz(buf);\n      while (!tz.empty() && (tz.back() == '\\n' || tz.back() == '\\r')) {\n        tz.pop_back();\n      }\n      if (!tz.empty()) {\n        return tz;\n      }

Comment thread testing/run_all_tests.cc
Comment on lines +98 to +102
std::string line(log);
if (!line.empty() && line.back() == '\n') {
line.pop_back(); // Remove newline.
}
LOG(INFO) << line;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Creating a std::string inside the loop for every line of the log file introduces unnecessary overhead, including potential heap allocations for lines exceeding the Small String Optimization (SSO) threshold. Since we only need to strip the trailing newline and log the result, we can perform this safely on the raw C-string buffer using strlen, which also avoids the need to include <string>.

      size_t len = strlen(log);\n      while (len > 0 && (log[len - 1] == '\\n' || log[len - 1] == '\\r')) {\n        log[--len] = '\\0';\n      }\n      LOG(INFO) << log;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant