-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathbuffer_overflow.cpp
More file actions
133 lines (119 loc) · 6 KB
/
Copy pathbuffer_overflow.cpp
File metadata and controls
133 lines (119 loc) · 6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Buffer overflow demos — mirrors docs/buffer_overflow.md.
//
// Buffer overflow = writing past the end of a buffer. In robotics this shows
// up as: a CAN frame larger than expected, a serial DMA stream that exceeds
// the ring-buffer slot, or a camera frame whose row stride was miscalculated
// (e.g. 640*3 vs 640*4 bytes when alpha is unexpectedly present).
//
// This binary DEMONSTRATES the APIs. The actually-unsafe demo is gated behind
// `--unsafe` so the program runs cleanly by default. Build with -fsanitize=address
// (or compile in Debug with stack-protector on) to see ASan / __stack_chk_fail
// catch the corruption when you do pass --unsafe.
#include <algorithm>
#include <array>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <span>
#include <string>
#include <string_view>
#include <vector>
namespace {
// ---------------------------------------------------------------------------
// 1. The classic vulnerable example from the doc: strcpy into a small buffer.
// Gated — running this with a long input would smash the stack.
// ---------------------------------------------------------------------------
void demo_unsafe_strcpy() {
std::cout << "[unsafe] strcpy into a 10-byte buffer with a 56-byte string\n";
char buffer[10];
const char *payload = "This is a very long string that exceeds the buffer size.";
// Compilers warn; ASan reports stack-buffer-overflow; -fstack-protector
// aborts via __stack_chk_fail. NEVER do this in real code.
std::strcpy(buffer, payload);
std::cout << " wrote: " << buffer << '\n';
}
// ---------------------------------------------------------------------------
// 2. Prevention #1: bounds checking. Check the size BEFORE copying.
// ---------------------------------------------------------------------------
void demo_bounds_check(std::string_view input) {
char buffer[10] = {};
if (input.size() >= sizeof(buffer)) {
std::cout << "[bounds] rejecting " << input.size()
<< "-byte input (capacity " << sizeof(buffer) - 1 << ")\n";
return;
}
std::memcpy(buffer, input.data(), input.size());
buffer[input.size()] = '\0';
std::cout << "[bounds] accepted: " << buffer << '\n';
}
// ---------------------------------------------------------------------------
// 3. Prevention #2: safer C functions. strncpy caps the copy length, but
// note the well-known gotcha — it does NOT null-terminate if the source
// fills the buffer. Always terminate explicitly.
// ---------------------------------------------------------------------------
void demo_strncpy() {
char buffer[10];
const char *payload = "This is a very long string that exceeds the buffer size.";
std::strncpy(buffer, payload, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0'; // guarantee termination
std::cout << "[strncpy] truncated to: " << buffer << '\n';
}
// ---------------------------------------------------------------------------
// 4. Prevention #3: dynamic allocation sized to the input. Equivalent to
// receiving a CAN/serial frame of advertised length `n` and allocating
// a matching scratch buffer instead of using a fixed stack array.
// ---------------------------------------------------------------------------
void demo_dynamic_alloc(std::string_view payload) {
std::vector<char> buf(payload.size() + 1);
std::memcpy(buf.data(), payload.data(), payload.size());
buf[payload.size()] = '\0';
std::cout << "[dynamic] " << buf.size() - 1 << " bytes copied safely\n";
}
// ---------------------------------------------------------------------------
// 5. Prevention #4: high-level constructs. std::string / std::vector own
// their storage and grow as needed — the idiomatic modern C++ answer.
// ---------------------------------------------------------------------------
void demo_high_level(std::string_view payload) {
std::string s(payload); // no overflow possible
std::cout << "[std::string] " << s.size() << " bytes, content: " << s << '\n';
}
// ---------------------------------------------------------------------------
// 6. Robotics example: image row stride miscalc. A 4-row RGB image at
// width=8 needs 96 bytes (8*3*4). If the producer assumed RGBA (stride 32)
// and the consumer assumes RGB (stride 24), the consumer reads past row 3.
// std::span + .subspan() makes the bounds explicit and catches it.
// ---------------------------------------------------------------------------
void demo_image_stride() {
constexpr std::size_t width = 8, height = 4, channels = 3;
std::array<std::uint8_t, width * height * channels> frame{};
std::span<std::uint8_t> view(frame);
for (std::size_t row = 0; row < height; ++row) {
auto row_view = view.subspan(row * width * channels, width * channels);
std::fill(row_view.begin(), row_view.end(), static_cast<std::uint8_t>(row));
}
std::cout << "[stride] filled " << height << " rows of " << width * channels
<< " bytes; last byte = " << int(frame.back()) << '\n';
// view.subspan(height * width * channels, 1) would throw/UB-trap — span
// gives us a single place to assert(offset + len <= size()).
}
} // namespace
int main(int argc, char **argv) {
const bool run_unsafe =
argc > 1 && std::string_view(argv[1]) == "--unsafe";
std::cout << "=== buffer overflow: APIs and defenses ===\n\n";
demo_bounds_check("ok");
demo_bounds_check("way too long for ten bytes");
demo_strncpy();
demo_dynamic_alloc("arbitrary length sensor payload");
demo_high_level("hello from std::string");
demo_image_stride();
std::cout << "\n--- unsafe demo (gated) ---\n";
if (run_unsafe) {
demo_unsafe_strcpy(); // ASan: stack-buffer-overflow; or SIGABRT via stack canary
} else {
std::cout << "skipped; pass --unsafe to run the strcpy overflow.\n"
" Build with -fsanitize=address to see the diagnostic,\n"
" or rely on -fstack-protector to abort via __stack_chk_fail.\n";
}
return 0;
}