Fix security vulnerabilities across codebase - #355
Conversation
- Add bounds checking for buffer access in ds5_fw_logger.cpp - Fix buffer overflow in hwmc.cpp (increased buffer size + validation) - Replace VLA with heap allocation in StreamView.cpp to prevent stack overflow - Add malloc null-check in StreamView.cpp - Quote shell variables and add validation in build_all.sh - Replace unsafe strcpy with strncpy in framesextract.c - Add input validation for device paths in test_fw_version.py - Add security warning for NOPASSWD sudoers in install.tegra.artifacts.sh - Use GitHub actor variable instead of hardcoded git identity in workflows Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Addresses multiple security issues across C/C++ utilities, shell scripts, tests, and CI workflows by adding bounds checks, safer allocations/copies, input validation, and safer scripting practices.
Changes:
- Hardened C/C++ codepaths against buffer/stack overflows and OOB reads (bounds checks, safer copies, heap allocations).
- Improved shell/script safety (quoting, basic validation) and added security warnings in install docs.
- Reduced risk in CI by using GitHub actor identity rather than hardcoded git config.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| utilities/streamApp/hwmc.cpp | Adds param bounds checks and output buffer length clamping. |
| utilities/streamApp/gui/StreamView.cpp | Replaces VLA with heap allocation; adds malloc null-check. |
| utilities/streamApp/ds5_fw_logger.cpp | Adds bounds checking to prevent out-of-bounds log access. |
| test/test_metadata/framesextract.c | Replaces unsafe strcpy with bounded strncpy + terminator. |
| test/test_fw_version.py | Adds device/path validation and safer subprocess argument usage. |
| test/install.tegra.artifacts.sh | Adds explicit security warning about NOPASSWD sudoers entry. |
| build_all.sh | Quotes variables and adds a guard before destructive rm -rf. |
| .github/workflows/build-jp6.yml | Uses ${{ github.actor }} for git identity in CI. |
| .github/workflows/build-jp6.2.yml | Uses ${{ github.actor }} for git identity in CI. |
| .github/workflows/build-jp6.1.yml | Uses ${{ github.actor }} for git identity in CI. |
| .github/workflows/build-jp512.yml | Uses ${{ github.actor }} for git identity in CI. |
| .github/workflows/build-jp502.yml | Uses ${{ github.actor }} for git identity in CI. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (inParams.empty()) { | ||
| cerr << "Error: HWMC requires at least an opcode" << endl; | ||
| return; | ||
| } | ||
| opcode = inParams[0]; | ||
| memset(params, 0, sizeof(params)); |
There was a problem hiding this comment.
The early return on empty inParams skips memset(params, ...), leaving params uninitialized in the constructed object. Initialize params unconditionally (e.g., memset before the empty-check or default-initialize the array) and consider making the “no opcode” case a construction failure (exception/factory/valid-flag) so callers don’t proceed with a partially-initialized HWMC.
| if (inParams.empty()) { | |
| cerr << "Error: HWMC requires at least an opcode" << endl; | |
| return; | |
| } | |
| opcode = inParams[0]; | |
| memset(params, 0, sizeof(params)); | |
| memset(params, 0, sizeof(params)); | |
| if (inParams.empty()) { | |
| cerr << "Error: HWMC requires at least an opcode" << endl; | |
| return; | |
| } | |
| opcode = inParams[0]; |
| void* ptr = malloc(bufferLength); | ||
| if (ptr == nullptr) { | ||
| RS_LOGE("Failed to allocate buffer %d of size %u", i, bufferLength); | ||
| return -1; | ||
| } | ||
| mRsBuffers.emplace_back(ptr, bufferLength, i); |
There was a problem hiding this comment.
On allocation failure, the function returns after potentially having already allocated earlier buffers in the loop, leaving the object in a partially-initialized state and potentially leaking memory/resources. Before returning, release/clear any previously allocated buffers (or ensure rollback via a scoped guard) so a failed start() doesn’t leave residual allocations.
| RS_LOGE("Invalid frame size: %u", frameSize); | ||
| return; | ||
| } | ||
| std::vector<char> imageVec(frameSize); |
There was a problem hiding this comment.
std::vector<char> imageVec(frameSize); allocates and zero-initializes a potentially large buffer on every processCaptureResult() call, which can be expensive on the hot capture path. Consider reusing a member buffer (resize only when needed) or using an uninitialized allocation strategy (where appropriate) to reduce per-frame allocations and memset costs.
| std::vector<char> imageVec(frameSize); | |
| static std::vector<char> imageVec; | |
| if (imageVec.size() < frameSize) { | |
| imageVec.resize(frameSize); | |
| } |
| dfu_device = subprocess.check_output(["ls", "/sys/class/d4xx-class/"]).decode() | ||
| assert "d4xx-dfu-" in dfu_device, "D4xx DFU device not found" | ||
|
|
||
| # Validate DFU device name to prevent path traversal | ||
| dfu_device_name = dfu_device.strip() | ||
| if not re.match(r'^d4xx-dfu-[0-9]+$', dfu_device_name): | ||
| raise ValueError(f"Invalid DFU device name: {dfu_device_name}") | ||
|
|
There was a problem hiding this comment.
ls /sys/class/d4xx-class/ can return multiple entries (multi-line output). After .strip(), dfu_device_name may still contain embedded newlines, causing the regex check to fail and the test to error even when valid DFU devices exist. Parse splitlines() and select a single matching entry (or iterate entries and pick the first d4xx-dfu-<n>), then build the /dev/... path from that chosen name.
| dfu_device = subprocess.check_output(["ls", "/sys/class/d4xx-class/"]).decode() | |
| assert "d4xx-dfu-" in dfu_device, "D4xx DFU device not found" | |
| # Validate DFU device name to prevent path traversal | |
| dfu_device_name = dfu_device.strip() | |
| if not re.match(r'^d4xx-dfu-[0-9]+$', dfu_device_name): | |
| raise ValueError(f"Invalid DFU device name: {dfu_device_name}") | |
| dfu_device_output = subprocess.check_output(["ls", "/sys/class/d4xx-class/"]).decode() | |
| # Parse ls output, which may contain multiple entries, and select a valid DFU device | |
| dfu_entries = [line.strip() for line in dfu_device_output.splitlines() if line.strip()] | |
| dfu_pattern = re.compile(r'^d4xx-dfu-[0-9]+$') | |
| dfu_device_name = None | |
| for entry in dfu_entries: | |
| if dfu_pattern.match(entry): | |
| dfu_device_name = entry | |
| break | |
| if dfu_device_name is None: | |
| raise AssertionError("D4xx DFU device not found") |
| rm -rf "$SRCS/out" | ||
| fi | ||
|
|
||
| mkdir -p $TEGRA_KERNEL_OUT |
There was a problem hiding this comment.
$TEGRA_KERNEL_OUT is still unquoted here. For consistency with the new hardening (and to avoid word-splitting/globbing issues), quote it: mkdir -p "$TEGRA_KERNEL_OUT".
| mkdir -p $TEGRA_KERNEL_OUT | |
| mkdir -p "$TEGRA_KERNEL_OUT" |
- Move memset before empty check in HWMC constructor to ensure params initialization - Add cleanup of allocated buffers on malloc failure in StreamView::start() - Use static buffer in processCaptureResult() to avoid per-frame allocation overhead - Parse ls output with splitlines() to handle multiple DFU device entries - Quote remaining unquoted $TEGRA_KERNEL_OUT in build_all.sh Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Thanks for the thorough review! I've addressed all 6 comments in commit 1595c7a:
|
| if (inParams.empty()) { | ||
| cerr << "Error: HWMC requires at least an opcode" << endl; | ||
| return; | ||
| } |
There was a problem hiding this comment.
Constructor emits ad-hoc stderr output and returns early when inParams is empty; avoid leaving console diagnostic output and surprising early-return behavior in a constructor.
Details
✨ AI Reasoning
A newly added branch in the HWMC constructor writes an error message to stderr and returns early when no opcode is provided. This introduces ad-hoc console output from a constructor and changes control flow during object construction, which can be considered a leftover ad-hoc debug/diagnostic output and surprising behavior in production code. The change also leaves the object constructed with default/zeroed fields after returning from the constructor body, making caller behavior dependent on side-effecting stderr output. Flagging focuses on the newly added console error and early return introduced by this change.
🔧 How do I fix it?
Remove debugging statements like console.log, debugger, dd(), or logic bypasses like || true. Keep legitimate logging for monitoring and error handling.
Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
Show Fix
Remediation - low confidence
This patch mitigates ad-hoc debug output and early return in a constructor by removing the stderr diagnostic message and empty parameter check that left the object in a partially initialized state.
| if (inParams.empty()) { | |
| cerr << "Error: HWMC requires at least an opcode" << endl; | |
| return; | |
| } |
Summary
Test plan
./build_all.sh --clean 6.2to verify shell script changes🤖 Generated with Claude Code