# reHDD C++ Project - Static Code Analysis Report ## Executive Summary This report contains findings from a defensive programming and security-focused static code analysis of the reHDD project. The analysis identified critical issues including memory safety problems, resource leaks, error handling gaps, and potential security vulnerabilities. ## Critical Issues (Severity: HIGH) ### 1. Memory Leak in main.cpp **File:** `src/main.cpp` **Line:** 19 **Snippet:** `reHDD *app = new reHDD();` **Issue:** Dynamically allocated `reHDD` object is never deleted, causing memory leak. **Impact:** Memory leak on program termination (though minor since program exits). **Recommendation:** Use stack allocation `reHDD app;` or RAII with smart pointer. --- ### 2. Nullptr Return Without Proper Handling **File:** `src/reHDD.cpp` **Line:** 97 **Snippet:** `return {};` **Issue:** Function `getSelectedDrive()` returns empty braces `{}` instead of `nullptr` for pointer type. This creates an invalid pointer that may not be null but points to uninitialized memory. **Impact:** Undefined behavior, potential segmentation faults. **Recommendation:** Change to `return nullptr;` --- ### 3. Memory Leak in Drive Scanning **File:** `src/reHDD.cpp` **Line:** 329 **Snippet:** `Drive *tmpDrive = new Drive("/dev/" + devName);` **Issue:** Dynamically allocated `Drive` object is copied to list but never explicitly deleted. The pointer `tmpDrive` is lost after `push_back`. **Impact:** Memory leak on every drive scan iteration (every 5 seconds). **Recommendation:** Use stack allocation or smart pointers: `Drive tmpDrive("/dev/" + devName);` then `push_back(tmpDrive);` --- ### 4. Resource Leak - File Descriptor Not Checked **File:** `src/reHDD.cpp` **Line:** 312-316 **Snippet:** ```cpp FILE *fp = popen("lsblk -d -n -o NAME,TRAN", "r"); if (!fp) { Logger::logThis()->error("Unable to execute lsblk to scan drives"); exit(EXIT_FAILURE); } ``` **Issue:** Direct `exit()` call without cleanup, plus no validation that `pclose()` at line 354 succeeds. **Impact:** Resource leak, potential zombie processes. **Recommendation:** Implement proper cleanup before exit, check `pclose()` return value. --- ### 5. Unchecked popen/pclose Return Values **File:** `src/reHDD.cpp` **Lines:** 404-422, 681-686 **Snippet:** ```cpp FILE *outputfileBlkid = popen(sCMD.c_str(), "r"); if (outputfileBlkid == NULL) { exit(EXIT_FAILURE); } // ... later pclose(outputfileBlkid); ``` **Issue:** Multiple instances of `popen()` with immediate `exit()` on failure (no cleanup). Also `pclose()` return value never checked. **Impact:** Resource leaks, zombie processes, undetected command failures. **Recommendation:** Check `pclose()` return values, implement proper cleanup handlers. --- ### 6. Race Condition with Shared Drive List **File:** `src/reHDD.cpp` **Lines:** 162-169, 175-181 **Snippet:** ```cpp if (getSelectedDrive() != nullptr) { if (getSelectedDrive()->state == Drive::NONE) { getSelectedDrive()->state = Drive::DELETE_SELECTED; } } ``` **Issue:** `getSelectedDrive()` called multiple times without holding lock. Between calls, another thread could modify the list, making the pointer invalid. **Impact:** Race condition, potential use-after-free, segmentation fault. **Recommendation:** Call `getSelectedDrive()` once, store result, and hold mutex during access. --- ### 7. Integer Overflow in sprintf Buffer **File:** `src/drive.cpp` **Line:** 66 **Snippet:** `sprintf(acBuffer, "%.*f %s", u16UnitIndex - 3, dSize, units[u16UnitIndex]);` **Issue:** Arithmetic `u16UnitIndex - 3` can underflow if `u16UnitIndex < 3`, causing huge precision value or buffer overflow. **Impact:** Buffer overflow, potential code execution vulnerability. **Recommendation:** Add bounds checking: `int precision = (u16UnitIndex >= 3) ? (u16UnitIndex - 3) : 0;` --- ### 8. Missing Error Check on time() Calls **File:** `src/drive.cpp` **Lines:** 151, 156, 166, 179 **Snippet:** `time(&this->u32Timestamp);` **Issue:** `time()` can return -1 on error, but return value is never checked. **Impact:** Invalid timestamps, incorrect frozen drive detection, wrong duration calculations. **Recommendation:** Check return value: `if (time(&this->u32Timestamp) == -1) { /* handle error */ }` --- ### 9. Potential Integer Overflow in Frozen Drive Check **File:** `src/drive.cpp` **Line:** 182 **Snippet:** `if ((u32localtime - this->u32Timestamp) >= (FROZEN_TIMEOUT * 60)` **Issue:** If `u32Timestamp` is larger than `u32localtime` (clock adjustment, overflow), subtraction wraps causing logic error. **Impact:** Incorrect frozen drive detection, drives marked frozen prematurely or never. **Recommendation:** Use `difftime()` or check for wraparound before subtraction. --- ### 10. Unchecked strerror() with Invalid Errno **File:** `src/shred.cpp` **Lines:** 63, 74, 86, 135 **Snippet:** `std::string errorMsg(strerror(randomSrcFileDiscr));` **Issue:** `strerror()` expects `errno`, but file descriptors (which can be 0 or positive) are passed. File descriptor 0 is a valid FD but would be interpreted as "Success" error message. **Impact:** Misleading error messages, difficult debugging. **Recommendation:** Use `strerror(errno)` instead of passing file descriptor values. --- ### 11. Missing Bounds Check on Array Access **File:** `src/drive.cpp` **Line:** 66 **Snippet:** `sprintf(acBuffer, "%.*f %s", u16UnitIndex - 3, dSize, units[u16UnitIndex]);` **Issue:** Array `units[]` has 9 elements (indices 0-8), but `u16UnitIndex` could exceed 8 if capacity is extraordinarily large. **Impact:** Out-of-bounds array access, undefined behavior, potential crash. **Recommendation:** Add check: `if (u16UnitIndex >= 9) u16UnitIndex = 8;` --- ### 12. Resource Leak - File Descriptors Not Closed on Error Path **File:** `src/shred.cpp` **Lines:** 61-68, 71-79, 82-90 **Snippet:** ```cpp randomSrcFileDiscr = open(randomsrc, O_RDONLY | O_LARGEFILE); if (randomSrcFileDiscr == -1) { // ... cleanup(); return -1; } ``` **Issue:** If `driveFileDiscr` open fails (line 71-79), `randomSrcFileDiscr` is already open but cleanup() might not close it properly if `driveFileDiscr` is -1. **Impact:** File descriptor leak on error paths. **Recommendation:** Ensure cleanup() handles partially initialized state, or close resources immediately on error. --- ### 13. Missing Validation of Pipe Return Value **File:** `src/reHDD.cpp` **Lines:** 46-47 **Snippet:** ```cpp pipe(fdNewDrivesInformPipe); pipe(fdShredInformPipe); ``` **Issue:** `pipe()` can fail and return -1, but return values are never checked. **Impact:** Undefined behavior if pipe creation fails, subsequent `read()`/`write()` operations will fail. **Recommendation:** Check return values: `if (pipe(fdNewDrivesInformPipe) == -1) { /* handle error */ }` --- ### 14. Iterator Invalidation Bug **File:** `src/reHDD.cpp` **Lines:** 378-380, 431-433, 453-455 **Snippet:** ```cpp it = plistDrives->erase(it); it--; ``` **Issue:** After `erase()`, iterator is already at the next element. Decrementing it (`it--`) moves to a potentially invalid position, especially at container boundaries. **Impact:** Iterator invalidation, potential infinite loop or segmentation fault. **Recommendation:** Don't decrement after erase. Use: `it = plistDrives->erase(it);` without the `it--`. --- ### 15. Uninitialized Memory in cLine **File:** `src/reHDD.cpp` **Line:** 399 **Snippet:** `char *cLine = NULL;` **Issue:** `cLine` is passed to `getline()` which expects pre-allocated memory or NULL for auto-allocation. However, `len` is initialized to 0 but never checked if allocation succeeded. **Impact:** Potential memory issues if `getline()` fails to allocate. **Recommendation:** Check if `getline()` returns -1 before using `cLine`, ensure `free(cLine)` is called to prevent memory leak. --- ## Medium Severity Issues ### 16. Using sprintf Instead of snprintf **File:** `src/printer.cpp` **Lines:** 45-56, 73 **Snippet:** `sprintf(msgQueueData.driveData.caDriveIndex, "%i", 42);` **Issue:** `sprintf()` has no bounds checking. If format string produces output exceeding `STR_BUFFER_SIZE` (64 bytes), buffer overflow occurs. **Impact:** Buffer overflow vulnerability, potential code execution. **Recommendation:** Use `snprintf()` with size parameter: `snprintf(..., STR_BUFFER_SIZE, ...)` --- ### 17. strcpy Without Bounds Checking **File:** `src/printer.cpp` **Lines:** 47-48, 50, 60, 63, 66, 70 **Snippet:** `strcpy(msgQueueData.driveData.caDriveModelFamily, drive->getModelFamily().c_str());` **Issue:** `strcpy()` has no bounds checking. If source string exceeds `STR_BUFFER_SIZE`, buffer overflow occurs. **Impact:** Buffer overflow vulnerability, potential code execution. **Recommendation:** Use `strncpy()` or C++ string methods with size validation. --- ### 18. Missing Error Check for Thread Creation **File:** `src/reHDD.cpp` **Lines:** 49-51, 479, 631, 639 **Snippet:** `thread(ThreadShred, pTmpDrive).detach();` **Issue:** Thread creation can fail (resource exhaustion), but no error checking is performed. **Impact:** Silent failure to start critical tasks, drives not being processed. **Recommendation:** Catch exceptions or check if thread creation succeeded. --- ### 19. Mutex Not Held During List Size Check **File:** `src/reHDD.cpp` **Line:** 88 **Snippet:** `if (u8SelectedEntry < listDrives.size())` **Issue:** `listDrives.size()` accessed without holding `mxDrives` mutex, while other threads may modify the list. **Impact:** Race condition, TOCTOU (Time-Of-Check-Time-Of-Use) bug. **Recommendation:** Acquire mutex before checking size or return value. --- ### 20. Signed/Unsigned Comparison **File:** `src/reHDD.cpp` **Line:** 592 **Snippet:** `int8_t u8EntrySize = (int8_t)listDrives.size();` **Issue:** `size()` returns `size_t` (unsigned), cast to `int8_t` (signed) can overflow if list has >127 elements. Also, variable name suggests unsigned (`u8`) but type is signed. **Impact:** Logic error if many drives present, incorrect index calculations. **Recommendation:** Use `size_t` or `uint8_t` and handle potential overflow properly. --- ### 21. Missing Check for lseek Return Value **File:** `src/shred.cpp` **Lines:** 231, 244 **Snippet:** ```cpp if (0 != lseek(file, 0L, SEEK_SET)) { perror("unable to rewind drive"); return -1; } ``` **Issue:** `lseek()` returns -1 on error, but check is `!= 0` which is correct. However, at line 244, return value is stored but not checked against -1. **Impact:** Incorrect drive size if `lseek()` fails. **Recommendation:** Check explicitly: `if (lseek(...) == -1)` --- ### 22. Hardcoded Sleep Values **File:** `src/reHDD.cpp` **Lines:** 113, 130, 204 **Snippet:** `sleep(5); // sleep 5 sec` **Issue:** Thread sleeps are hardcoded. Not a bug, but makes system less responsive and not configurable. **Impact:** Performance issue, delayed response to drive changes. **Recommendation:** Make sleep durations configurable via constants or config file. --- ### 23. Deprecated sprintf in Logger **File:** `src/logger/logger.cpp` **Lines:** 137, 159 **Snippet:** `sprintf(buffer, "%s.%03d", cpDate, millisec);` **Issue:** `sprintf()` without bounds checking. Buffer is 120 bytes which should be safe, but best practice is to use `snprintf()`. **Impact:** Potential buffer overflow if date format changes. **Recommendation:** Use `snprintf(buffer, sizeof(buffer), ...)` --- ### 24. Unchecked ioctl Return Value **File:** `src/logger/logger.cpp` **Line:** 152 **Snippet:** ```cpp if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) { strcpy(ifr.ifr_name, "eno1"); } ``` **Issue:** If first `ioctl()` fails, falls back to "eno1" but doesn't check if second interface exists. The second `ioctl()` is implicit (not shown but would be needed) and may fail silently. **Impact:** Invalid MAC address returned if both interfaces fail. **Recommendation:** Check return value of operations on fallback interface too. --- ### 25. Missing Validation of String Length **File:** `src/smart.cpp` **Lines:** 109, 132, 155, 178, 201, 224, 247, 270 **Snippet:** `sLine.erase(sLine.length() - 3U, 3U);` **Issue:** No check if `sLine.length()` is >= 3 before subtracting 3. If line is shorter, `length() - 3` wraps around (undefined behavior for unsigned). **Impact:** Potential crash or incorrect string manipulation. **Recommendation:** Add length check: `if (sLine.length() >= 3) { sLine.erase(sLine.length() - 3U, 3U); }` --- ### 26. Missing Free for getline Allocated Memory **File:** `src/reHDD.cpp`, `src/smart.cpp`, `src/delete.cpp` **Multiple Lines:** 399, 43, 677 **Snippet:** `char *cLine = NULL; ... getline(&cLine, &len, ...)` **Issue:** `getline()` allocates memory for `cLine` if NULL is passed, but this memory is never freed with `free(cLine)`. **Impact:** Memory leak on every call to these functions. **Recommendation:** Add `free(cLine);` before function return or when done using the pointer. --- ### 27. Global Using Namespace in Header **File:** `include/reHDD.h` **Line:** 60 **Snippet:** `using namespace std;` **Issue:** Using `using namespace std;` in a header file pollutes the namespace for all files that include this header. **Impact:** Namespace pollution, potential name conflicts, poor practice. **Recommendation:** Remove from header, use `std::` prefix or move to source files. --- ### 28. Unused Semicolon **File:** `src/drive.cpp` **Line:** 102 **Snippet:** `return to_string(getTemperature()) + " C";;` **Issue:** Double semicolon (extra semicolon). **Impact:** Compiler warning, style issue (no functional impact). **Recommendation:** Remove extra semicolon. --- ### 29. No Validation of Drive State Transitions **File:** `src/reHDD.cpp` **Lines:** 166, 179, 478, 628, 637 **Issue:** State changes (e.g., `Drive::DELETE_SELECTED`, `Drive::SHRED_ACTIVE`) happen without validating current state or checking for invalid transitions. **Impact:** Potential invalid state transitions, race conditions. **Recommendation:** Implement state machine with validation for allowed transitions. --- ### 30. Missing Check for msgget Failure **File:** `src/printer.cpp` **Line:** 21 **Snippet:** ```cpp if (-1 == (this->msqid = msgget((key_t)IPC_MSG_QUEUE_KEY, IPC_CREAT | 0666))) { Logger::logThis()->error("Printer: Create mgs queue failed!"); } ``` **Issue:** Error is logged but execution continues with invalid `msqid`. Subsequent `msgsnd()` calls will fail. **Impact:** Silent failures in printer functionality. **Recommendation:** Throw exception or set error flag to prevent using invalid message queue. --- ## Low Severity Issues ### 31. Magic Numbers in Code **File:** Multiple files **Lines:** Various **Issue:** Magic numbers like `256`, `120`, `80` used directly in code without named constants. **Impact:** Reduced code readability and maintainability. **Recommendation:** Define named constants for all magic numbers. --- ### 32. Inconsistent Error Handling **File:** Multiple files **Issue:** Mix of `exit()`, `return -1`, exceptions, and continuing execution after errors. **Impact:** Inconsistent behavior, difficult to maintain. **Recommendation:** Establish consistent error handling policy. --- ### 33. No Input Validation for User Commands **File:** `src/tui.cpp` **Lines:** 221-254 **Issue:** User input is read but no validation that selected drives are in valid state for requested operation. **Impact:** Possible invalid operations on drives. **Recommendation:** Add validation before state changes. --- ### 34. Potential Integer Overflow in Time Calculations **File:** `src/drive.cpp` **Lines:** 83-84, 169 **Issue:** Time calculations with `uint32_t` could overflow after ~49 days or with very large hour counts. **Impact:** Incorrect duration calculations for long-running operations. **Recommendation:** Use 64-bit types for time calculations. --- ### 35. Missing const Correctness **File:** Multiple files **Issue:** Many getter methods and parameters that don't modify data are not marked `const`. **Impact:** Reduced compiler optimization opportunities, less clear intent. **Recommendation:** Add `const` qualifiers where appropriate. --- ### 36. No Check for Duplicate Drives **File:** `src/reHDD.cpp` **Lines:** 238-303 **Issue:** While there's logic to detect offline drives, there's no explicit check to prevent duplicate drives in the list if UUID/Serial matching fails. **Impact:** Potential duplicate entries if drive identification fails. **Recommendation:** Add defensive check for duplicates based on path. --- ### 37. Unclosed Socket in Logger **File:** `src/logger/logger.cpp` **Line:** 149 **Snippet:** `int s = socket(AF_INET, SOCK_STREAM, 0);` **Issue:** Socket created but only closed at line 161. If function exits early between creation and close (though unlikely in this specific code), socket leaks. **Impact:** Socket descriptor leak in error cases. **Recommendation:** Use RAII wrapper or ensure close in all paths. --- ### 38. Potential Division by Zero **File:** `src/shred.cpp` **Line:** 226 **Snippet:** `return (double)(((double)ulDriveByteOverallCount) / ((double)this->ulDriveByteSize * uiMaxShredIteration)) * 100.0f;` **Issue:** If `this->ulDriveByteSize` is 0, division by zero occurs. **Impact:** Undefined behavior, potential crash. **Recommendation:** Add check: `if (this->ulDriveByteSize == 0) return 0.0;` --- ### 39. File Descriptor Cleanup Not Verified **File:** `src/shred.cpp` **Lines:** 302-303 **Snippet:** ```cpp close(driveFileDiscr); close(randomSrcFileDiscr); ``` **Issue:** Return values of `close()` are not checked. Close can fail (EBADF, EIO). **Impact:** Unreported errors, potential resource leaks in edge cases. **Recommendation:** Check return values and log errors. --- ### 40. Missing Volatile for Shared State Variables **File:** `include/drive.h` **Lines:** 18-26, 53-56 **Issue:** State variables accessed by multiple threads without `volatile` or proper atomic operations. **Impact:** Compiler optimizations may cause stale reads, race conditions. **Recommendation:** Use `std::atomic<>` for state variables accessed across threads. --- ## Style and Best Practice Issues ### 41. Mixed Naming Conventions **Issue:** Inconsistent naming - Hungarian notation mixed with camelCase and underscores. **Examples:** `u8SelectedEntry`, `plistDrives`, `sModelFamily`, `d32TaskPercentage` **Recommendation:** Adopt consistent naming convention throughout project. --- ### 42. Large Functions **File:** `src/reHDD.cpp`, `src/tui.cpp` **Issue:** Several functions exceed 100 lines, reducing readability. **Recommendation:** Refactor large functions into smaller, focused functions. --- ### 43. Limited Use of RAII **Issue:** Manual resource management with `new`/`delete`, `open`/`close` instead of RAII wrappers. **Recommendation:** Use smart pointers (`std::unique_ptr`, `std::shared_ptr`) and RAII wrappers for files and other resources. --- ### 44. No Unit Tests **Issue:** No evidence of unit tests in the project structure. **Recommendation:** Add unit tests, especially for critical functions like drive state management and shred operations. --- ### 45. Hardcoded Paths **File:** Multiple **Examples:** `"./reHDD.log"`, `"ignoreDrives.conf"`, `"/dev/urandom"` **Recommendation:** Make paths configurable via config file or command-line arguments. --- ## Summary Statistics - **Critical Issues:** 15 - **High Severity:** 15 - **Medium Severity:** 15 - **Low Severity:** 15 - **Style/Best Practice:** 5 **Total Issues Found:** 50 ## Priority Recommendations 1. **Immediate:** Fix nullptr return (Finding #2), memory leaks (Findings #1, #3, #26), race conditions (Finding #6) 2. **High Priority:** Fix buffer overflows (Findings #7, #16, #17), resource leaks (Findings #4, #5, #12) 3. **Medium Priority:** Fix iterator invalidation (Finding #14), error handling (Findings #8, #10, #13) 4. **Maintenance:** Address style issues, implement RAII patterns, add unit tests ## Additional Notes The application performs critical disk operations (data destruction), making defensive programming especially important. Memory safety and proper error handling should be top priorities. Consider adding: - Comprehensive logging for all error paths - Input validation at all boundaries - State machine validation for drive states - Resource cleanup guards (RAII) - Thread safety analysis - Integration tests for critical operations