red64-cli 0.3.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +194 -338
- package/dist/cli/parseArgs.d.ts.map +1 -1
- package/dist/cli/parseArgs.js +5 -13
- package/dist/cli/parseArgs.js.map +1 -1
- package/dist/components/init/types.d.ts +0 -2
- package/dist/components/init/types.d.ts.map +1 -1
- package/dist/components/screens/HelpScreen.d.ts.map +1 -1
- package/dist/components/screens/HelpScreen.js +0 -2
- package/dist/components/screens/HelpScreen.js.map +1 -1
- package/dist/components/screens/InitScreen.d.ts.map +1 -1
- package/dist/components/screens/InitScreen.js +5 -8
- package/dist/components/screens/InitScreen.js.map +1 -1
- package/dist/components/screens/StartScreen.d.ts.map +1 -1
- package/dist/components/screens/StartScreen.js +29 -8
- package/dist/components/screens/StartScreen.js.map +1 -1
- package/dist/components/screens/StatusScreen.d.ts.map +1 -1
- package/dist/components/screens/StatusScreen.js +16 -1
- package/dist/components/screens/StatusScreen.js.map +1 -1
- package/dist/services/AgentInvoker.d.ts.map +1 -1
- package/dist/services/AgentInvoker.js +76 -37
- package/dist/services/AgentInvoker.js.map +1 -1
- package/dist/services/ClaudeErrorDetector.d.ts +1 -1
- package/dist/services/ClaudeErrorDetector.d.ts.map +1 -1
- package/dist/services/ClaudeErrorDetector.js +1 -0
- package/dist/services/ClaudeErrorDetector.js.map +1 -1
- package/dist/services/ClaudeHealthCheck.d.ts +7 -0
- package/dist/services/ClaudeHealthCheck.d.ts.map +1 -1
- package/dist/services/ClaudeHealthCheck.js +76 -12
- package/dist/services/ClaudeHealthCheck.js.map +1 -1
- package/dist/services/ConfigService.d.ts +1 -0
- package/dist/services/ConfigService.d.ts.map +1 -1
- package/dist/services/ConfigService.js.map +1 -1
- package/dist/services/DockerRunner.js +1 -1
- package/dist/services/DockerRunner.js.map +1 -1
- package/dist/services/PhaseExecutor.d.ts.map +1 -1
- package/dist/services/PhaseExecutor.js +2 -1
- package/dist/services/PhaseExecutor.js.map +1 -1
- package/dist/services/TaskRunner.d.ts.map +1 -1
- package/dist/services/TaskRunner.js +2 -1
- package/dist/services/TaskRunner.js.map +1 -1
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/index.js.map +1 -1
- package/dist/types/index.d.ts +4 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/framework/stacks/c/code-quality.md +326 -0
- package/framework/stacks/c/coding-style.md +347 -0
- package/framework/stacks/c/conventions.md +513 -0
- package/framework/stacks/c/error-handling.md +350 -0
- package/framework/stacks/c/feedback.md +158 -0
- package/framework/stacks/c/memory-safety.md +408 -0
- package/framework/stacks/c/tech.md +122 -0
- package/framework/stacks/c/testing.md +472 -0
- package/framework/stacks/cpp/code-quality.md +282 -0
- package/framework/stacks/cpp/coding-style.md +363 -0
- package/framework/stacks/cpp/conventions.md +420 -0
- package/framework/stacks/cpp/error-handling.md +264 -0
- package/framework/stacks/cpp/feedback.md +104 -0
- package/framework/stacks/cpp/memory-safety.md +351 -0
- package/framework/stacks/cpp/tech.md +160 -0
- package/framework/stacks/cpp/testing.md +323 -0
- package/framework/stacks/java/code-quality.md +357 -0
- package/framework/stacks/java/coding-style.md +400 -0
- package/framework/stacks/java/conventions.md +437 -0
- package/framework/stacks/java/error-handling.md +408 -0
- package/framework/stacks/java/feedback.md +180 -0
- package/framework/stacks/java/tech.md +126 -0
- package/framework/stacks/java/testing.md +485 -0
- package/framework/stacks/javascript/async-patterns.md +216 -0
- package/framework/stacks/javascript/code-quality.md +182 -0
- package/framework/stacks/javascript/coding-style.md +293 -0
- package/framework/stacks/javascript/conventions.md +268 -0
- package/framework/stacks/javascript/error-handling.md +216 -0
- package/framework/stacks/javascript/feedback.md +80 -0
- package/framework/stacks/javascript/tech.md +114 -0
- package/framework/stacks/javascript/testing.md +209 -0
- package/framework/stacks/loco/code-quality.md +156 -0
- package/framework/stacks/loco/coding-style.md +247 -0
- package/framework/stacks/loco/error-handling.md +225 -0
- package/framework/stacks/loco/feedback.md +35 -0
- package/framework/stacks/loco/loco.md +342 -0
- package/framework/stacks/loco/structure.md +193 -0
- package/framework/stacks/loco/tech.md +129 -0
- package/framework/stacks/loco/testing.md +211 -0
- package/framework/stacks/rust/code-quality.md +370 -0
- package/framework/stacks/rust/coding-style.md +475 -0
- package/framework/stacks/rust/conventions.md +430 -0
- package/framework/stacks/rust/error-handling.md +399 -0
- package/framework/stacks/rust/feedback.md +152 -0
- package/framework/stacks/rust/memory-safety.md +398 -0
- package/framework/stacks/rust/tech.md +121 -0
- package/framework/stacks/rust/testing.md +528 -0
- package/package.json +14 -2
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
# Memory Safety
|
|
2
|
+
|
|
3
|
+
Patterns and practices for memory-safe C code. Manual memory management demands discipline: always check allocations, always free on all paths, and use tools to verify correctness.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Philosophy
|
|
8
|
+
|
|
9
|
+
- **Every allocation has an owner**: One function allocates, one function frees -- document which
|
|
10
|
+
- **Check every malloc**: Never assume allocation succeeds
|
|
11
|
+
- **Free and NULL**: After freeing, set the pointer to NULL to prevent use-after-free
|
|
12
|
+
- **Verify with tools**: Valgrind, ASan, and UBSan catch what code review misses
|
|
13
|
+
- **Ref**: CERT C MEM30-C through MEM36-C, SEI CERT Secure Coding
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Always Check malloc Return
|
|
18
|
+
|
|
19
|
+
```c
|
|
20
|
+
/* GOOD: Check allocation and handle failure */
|
|
21
|
+
char *buffer = malloc(size);
|
|
22
|
+
if (!buffer) {
|
|
23
|
+
log_error("malloc(%zu) failed", size);
|
|
24
|
+
return ERR_NOMEM;
|
|
25
|
+
}
|
|
26
|
+
memset(buffer, 0, size);
|
|
27
|
+
|
|
28
|
+
/* GOOD: calloc initializes to zero and checks for overflow */
|
|
29
|
+
user_t *users = calloc(count, sizeof(user_t));
|
|
30
|
+
if (!users) {
|
|
31
|
+
return ERR_NOMEM;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* BAD: No check -- null dereference if allocation fails */
|
|
35
|
+
char *buffer = malloc(size);
|
|
36
|
+
memset(buffer, 0, size); /* Crash if malloc returned NULL */
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Free and NULL Pattern
|
|
42
|
+
|
|
43
|
+
```c
|
|
44
|
+
/* GOOD: Free and immediately NULL the pointer */
|
|
45
|
+
void user_free(user_t *user) {
|
|
46
|
+
if (!user) return;
|
|
47
|
+
free(user->name);
|
|
48
|
+
user->name = NULL;
|
|
49
|
+
free(user->email);
|
|
50
|
+
user->email = NULL;
|
|
51
|
+
free(user);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* GOOD: Safe free macro */
|
|
55
|
+
#define SAFE_FREE(ptr) \
|
|
56
|
+
do { \
|
|
57
|
+
free(ptr); \
|
|
58
|
+
(ptr) = NULL; \
|
|
59
|
+
} while (0)
|
|
60
|
+
|
|
61
|
+
/* Usage */
|
|
62
|
+
SAFE_FREE(buffer);
|
|
63
|
+
SAFE_FREE(name);
|
|
64
|
+
|
|
65
|
+
/* BAD: Free without NULL -- dangling pointer */
|
|
66
|
+
free(buffer);
|
|
67
|
+
/* buffer still holds old address -- use-after-free risk */
|
|
68
|
+
if (buffer) { /* This check is MEANINGLESS after free */
|
|
69
|
+
use(buffer);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Goto Cleanup for Resource Safety
|
|
76
|
+
|
|
77
|
+
The `goto cleanup` pattern ensures all resources are freed on every exit path. See error-handling.md for full details.
|
|
78
|
+
|
|
79
|
+
```c
|
|
80
|
+
/* GOOD: All resources freed regardless of which step failed */
|
|
81
|
+
int process(const char *input, result_t *out) {
|
|
82
|
+
int rc = ERR_OK;
|
|
83
|
+
char *buffer = NULL;
|
|
84
|
+
FILE *fp = NULL;
|
|
85
|
+
cJSON *json = NULL;
|
|
86
|
+
|
|
87
|
+
buffer = malloc(BUFFER_SIZE);
|
|
88
|
+
if (!buffer) { rc = ERR_NOMEM; goto cleanup; }
|
|
89
|
+
|
|
90
|
+
fp = fopen(input, "r");
|
|
91
|
+
if (!fp) { rc = ERR_IO; goto cleanup; }
|
|
92
|
+
|
|
93
|
+
/* ... use buffer and fp ... */
|
|
94
|
+
|
|
95
|
+
json = cJSON_Parse(buffer);
|
|
96
|
+
if (!json) { rc = ERR_VALIDATION; goto cleanup; }
|
|
97
|
+
|
|
98
|
+
rc = extract(json, out);
|
|
99
|
+
|
|
100
|
+
cleanup:
|
|
101
|
+
if (json) cJSON_Delete(json);
|
|
102
|
+
if (fp) fclose(fp);
|
|
103
|
+
free(buffer); /* free(NULL) is safe per C standard */
|
|
104
|
+
return rc;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* BAD: Early returns leak resources */
|
|
108
|
+
int process(const char *input, result_t *out) {
|
|
109
|
+
char *buffer = malloc(BUFFER_SIZE);
|
|
110
|
+
if (!buffer) return ERR_NOMEM;
|
|
111
|
+
|
|
112
|
+
FILE *fp = fopen(input, "r");
|
|
113
|
+
if (!fp) return ERR_IO; /* LEAK: buffer not freed */
|
|
114
|
+
|
|
115
|
+
/* ... */
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Buffer Overflow Prevention
|
|
122
|
+
|
|
123
|
+
```c
|
|
124
|
+
/* GOOD: Use snprintf instead of sprintf */
|
|
125
|
+
char message[256];
|
|
126
|
+
snprintf(message, sizeof(message), "User %s logged in from %s", name, ip);
|
|
127
|
+
|
|
128
|
+
/* GOOD: Use strncpy with explicit null termination */
|
|
129
|
+
char dest[64];
|
|
130
|
+
strncpy(dest, src, sizeof(dest) - 1);
|
|
131
|
+
dest[sizeof(dest) - 1] = '\0';
|
|
132
|
+
|
|
133
|
+
/* GOOD: Use strncat with remaining space calculation */
|
|
134
|
+
char path[PATH_MAX];
|
|
135
|
+
strncpy(path, base_dir, sizeof(path) - 1);
|
|
136
|
+
path[sizeof(path) - 1] = '\0';
|
|
137
|
+
strncat(path, "/", sizeof(path) - strlen(path) - 1);
|
|
138
|
+
strncat(path, filename, sizeof(path) - strlen(path) - 1);
|
|
139
|
+
|
|
140
|
+
/* GOOD: Bounds-check array access */
|
|
141
|
+
int get_item(const int *arr, size_t arr_len, size_t index) {
|
|
142
|
+
if (index >= arr_len) {
|
|
143
|
+
return ERR_OUT_OF_BOUNDS;
|
|
144
|
+
}
|
|
145
|
+
return arr[index];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* BAD: sprintf -- no bounds checking */
|
|
149
|
+
char message[64];
|
|
150
|
+
sprintf(message, "User %s logged in from %s", name, ip); /* Buffer overflow */
|
|
151
|
+
|
|
152
|
+
/* BAD: strcpy -- no bounds checking */
|
|
153
|
+
char dest[64];
|
|
154
|
+
strcpy(dest, src); /* Overflow if src > 63 chars */
|
|
155
|
+
|
|
156
|
+
/* BAD: gets -- never use, removed in C11 */
|
|
157
|
+
char input[256];
|
|
158
|
+
gets(input); /* Unbounded read from stdin */
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Banned Functions
|
|
162
|
+
|
|
163
|
+
| Banned | Replacement | Reason |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| `sprintf` | `snprintf` | No bounds checking |
|
|
166
|
+
| `strcpy` | `strncpy` + null term, or `snprintf` | No bounds checking |
|
|
167
|
+
| `strcat` | `strncat` or `snprintf` | No bounds checking |
|
|
168
|
+
| `gets` | `fgets` | Removed in C11, unbounded |
|
|
169
|
+
| `scanf("%s", ...)` | `scanf("%63s", ...)` with width | Unbounded string read |
|
|
170
|
+
| `atoi`, `atol` | `strtol`, `strtoul` | No error detection |
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Valgrind Memcheck
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# Full leak check
|
|
178
|
+
valgrind --leak-check=full \
|
|
179
|
+
--show-leak-kinds=all \
|
|
180
|
+
--track-origins=yes \
|
|
181
|
+
--error-exitcode=1 \
|
|
182
|
+
./build/myapp
|
|
183
|
+
|
|
184
|
+
# With suppression file
|
|
185
|
+
valgrind --leak-check=full \
|
|
186
|
+
--suppressions=valgrind.supp \
|
|
187
|
+
--error-exitcode=1 \
|
|
188
|
+
./build/tests/test_user
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### What Valgrind Detects
|
|
192
|
+
|
|
193
|
+
| Issue | Example |
|
|
194
|
+
|---|---|
|
|
195
|
+
| Memory leak | `malloc` without `free` |
|
|
196
|
+
| Use-after-free | Accessing freed memory |
|
|
197
|
+
| Invalid read/write | Buffer overflow, out-of-bounds |
|
|
198
|
+
| Uninitialized value | Reading memory before writing |
|
|
199
|
+
| Double free | Calling `free` twice on same pointer |
|
|
200
|
+
| Mismatched free | `free` on stack memory or wrong allocator |
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## AddressSanitizer (ASan)
|
|
205
|
+
|
|
206
|
+
Faster than Valgrind, compile-time instrumentation.
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# Build with ASan
|
|
210
|
+
cmake -B build-asan -DCMAKE_BUILD_TYPE=Debug \
|
|
211
|
+
-DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer"
|
|
212
|
+
cmake --build build-asan
|
|
213
|
+
|
|
214
|
+
# Run tests under ASan
|
|
215
|
+
ctest --test-dir build-asan --output-on-failure
|
|
216
|
+
|
|
217
|
+
# Enable leak detection (default on Linux, opt-in on macOS)
|
|
218
|
+
ASAN_OPTIONS=detect_leaks=1 ./build-asan/myapp
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### ASan Detects
|
|
222
|
+
|
|
223
|
+
- Heap buffer overflow
|
|
224
|
+
- Stack buffer overflow
|
|
225
|
+
- Global buffer overflow
|
|
226
|
+
- Use-after-free
|
|
227
|
+
- Use-after-scope
|
|
228
|
+
- Double free
|
|
229
|
+
- Memory leaks (with `detect_leaks=1`)
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Arena / Pool Allocators
|
|
234
|
+
|
|
235
|
+
For performance-critical paths, arena allocators avoid per-object free overhead and eliminate fragmentation.
|
|
236
|
+
|
|
237
|
+
```c
|
|
238
|
+
/* Simple arena allocator */
|
|
239
|
+
typedef struct {
|
|
240
|
+
char *base;
|
|
241
|
+
size_t capacity;
|
|
242
|
+
size_t offset;
|
|
243
|
+
} arena_t;
|
|
244
|
+
|
|
245
|
+
int arena_init(arena_t *a, size_t capacity) {
|
|
246
|
+
a->base = malloc(capacity);
|
|
247
|
+
if (!a->base) return ERR_NOMEM;
|
|
248
|
+
a->capacity = capacity;
|
|
249
|
+
a->offset = 0;
|
|
250
|
+
return ERR_OK;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
void *arena_alloc(arena_t *a, size_t size) {
|
|
254
|
+
/* Align to 16 bytes */
|
|
255
|
+
size_t aligned = (size + 15) & ~(size_t)15;
|
|
256
|
+
if (a->offset + aligned > a->capacity) {
|
|
257
|
+
return NULL; /* Out of space */
|
|
258
|
+
}
|
|
259
|
+
void *ptr = a->base + a->offset;
|
|
260
|
+
a->offset += aligned;
|
|
261
|
+
return ptr;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
void arena_reset(arena_t *a) {
|
|
265
|
+
a->offset = 0; /* "Free" everything at once */
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
void arena_destroy(arena_t *a) {
|
|
269
|
+
free(a->base);
|
|
270
|
+
a->base = NULL;
|
|
271
|
+
a->capacity = 0;
|
|
272
|
+
a->offset = 0;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* Usage: request-scoped allocation */
|
|
276
|
+
int handle_request(const request_t *req, response_t *resp) {
|
|
277
|
+
arena_t arena;
|
|
278
|
+
if (arena_init(&arena, 64 * 1024) != ERR_OK) {
|
|
279
|
+
return ERR_NOMEM;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
char *name = arena_alloc(&arena, 256);
|
|
283
|
+
char *body = arena_alloc(&arena, 4096);
|
|
284
|
+
/* ... use name and body ... */
|
|
285
|
+
|
|
286
|
+
arena_destroy(&arena); /* Single free for all allocations */
|
|
287
|
+
return ERR_OK;
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## RAII-Like Patterns with GCC/Clang Cleanup Attribute
|
|
294
|
+
|
|
295
|
+
```c
|
|
296
|
+
/* GCC/Clang extension: __attribute__((cleanup)) */
|
|
297
|
+
static void free_ptr(void *p) {
|
|
298
|
+
free(*(void **)p);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
static void close_file(FILE **fp) {
|
|
302
|
+
if (*fp) fclose(*fp);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
#define AUTO_FREE __attribute__((cleanup(free_ptr)))
|
|
306
|
+
#define AUTO_CLOSE __attribute__((cleanup(close_file)))
|
|
307
|
+
|
|
308
|
+
/* Usage: automatic cleanup on scope exit */
|
|
309
|
+
int read_config(const char *path, config_t *out) {
|
|
310
|
+
AUTO_FREE char *buffer = malloc(4096);
|
|
311
|
+
if (!buffer) return ERR_NOMEM;
|
|
312
|
+
|
|
313
|
+
AUTO_CLOSE FILE *fp = fopen(path, "r");
|
|
314
|
+
if (!fp) return ERR_IO;
|
|
315
|
+
|
|
316
|
+
size_t n = fread(buffer, 1, 4095, fp);
|
|
317
|
+
buffer[n] = '\0';
|
|
318
|
+
|
|
319
|
+
return parse_config(buffer, out);
|
|
320
|
+
/* fp closed and buffer freed automatically at scope exit */
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* GOOD: Scope guard macro for arbitrary cleanup */
|
|
324
|
+
#define DEFER(fn) __attribute__((cleanup(fn)))
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**Note**: `__attribute__((cleanup))` is a GCC/Clang extension, not standard C. Use for internal code; prefer goto cleanup for portable library code.
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Dangling Pointer Prevention
|
|
332
|
+
|
|
333
|
+
```c
|
|
334
|
+
/* GOOD: NULL after free prevents use-after-free */
|
|
335
|
+
free(user->name);
|
|
336
|
+
user->name = NULL;
|
|
337
|
+
|
|
338
|
+
/* GOOD: Invalidate pointer after transferring ownership */
|
|
339
|
+
connection_t *conn = connection_create();
|
|
340
|
+
pool_add(pool, conn); /* Pool now owns conn */
|
|
341
|
+
conn = NULL; /* Prevent accidental use */
|
|
342
|
+
|
|
343
|
+
/* BAD: Returning pointer to local stack variable */
|
|
344
|
+
char *get_greeting(void) {
|
|
345
|
+
char buf[64];
|
|
346
|
+
snprintf(buf, sizeof(buf), "Hello, World!");
|
|
347
|
+
return buf; /* DANGLING: buf is on stack, destroyed on return */
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* GOOD: Return heap-allocated or caller-provided buffer */
|
|
351
|
+
char *get_greeting(void) {
|
|
352
|
+
char *buf = malloc(64);
|
|
353
|
+
if (!buf) return NULL;
|
|
354
|
+
snprintf(buf, 64, "Hello, World!");
|
|
355
|
+
return buf; /* Caller must free */
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
int get_greeting(char *buf, size_t buf_size) {
|
|
359
|
+
snprintf(buf, buf_size, "Hello, World!");
|
|
360
|
+
return ERR_OK;
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Double-Free Prevention
|
|
367
|
+
|
|
368
|
+
```c
|
|
369
|
+
/* GOOD: Idempotent destroy function */
|
|
370
|
+
void resource_destroy(resource_t **res) {
|
|
371
|
+
if (!res || !*res) return;
|
|
372
|
+
|
|
373
|
+
free((*res)->data);
|
|
374
|
+
free(*res);
|
|
375
|
+
*res = NULL; /* Prevents double-free if called again */
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/* Usage */
|
|
379
|
+
resource_t *r = resource_create();
|
|
380
|
+
resource_destroy(&r); /* r is now NULL */
|
|
381
|
+
resource_destroy(&r); /* Safe: no-op because r is NULL */
|
|
382
|
+
|
|
383
|
+
/* BAD: Non-idempotent free */
|
|
384
|
+
void resource_destroy(resource_t *res) {
|
|
385
|
+
free(res->data); /* Crash on double call */
|
|
386
|
+
free(res);
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## Anti-Patterns
|
|
393
|
+
|
|
394
|
+
| Anti-Pattern | Problem | Correct Approach |
|
|
395
|
+
|---|---|---|
|
|
396
|
+
| Unchecked `malloc` | Null dereference crash | Always check return, handle `ERR_NOMEM` |
|
|
397
|
+
| `free()` without NULL | Dangling pointer, use-after-free | `SAFE_FREE` macro or manual NULL after free |
|
|
398
|
+
| `sprintf`, `strcpy`, `strcat` | Buffer overflow | `snprintf`, `strncpy` + null term |
|
|
399
|
+
| Returning pointer to stack variable | Dangling pointer, UB | Return heap-allocated or use output parameter |
|
|
400
|
+
| No Valgrind/ASan in CI | Memory bugs reach production | Run sanitizers on every test |
|
|
401
|
+
| Manual cleanup at each error point | Missed frees, duplicated code | `goto cleanup` pattern |
|
|
402
|
+
| Mixing allocators | Heap corruption | Match `malloc`/`free`, `calloc`/`free`, custom pairs |
|
|
403
|
+
| VLA (variable-length arrays) | Stack overflow, no error handling | Use `malloc` with size check |
|
|
404
|
+
| `realloc` without temp pointer | Leak on failure | `tmp = realloc(ptr, n); if (tmp) ptr = tmp;` |
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
_Memory safety in C is achieved through discipline, patterns, and tools. Check every allocation, free on every path, NULL after free, and verify with sanitizers._
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Technology Stack
|
|
2
|
+
|
|
3
|
+
## Architecture
|
|
4
|
+
|
|
5
|
+
Modern C application with safety-first design. C17 as baseline standard (C23 where compiler support allows), CMake for build system, comprehensive static analysis and sanitizer pipeline, structured testing with Unity or CMocka.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Technologies
|
|
10
|
+
|
|
11
|
+
- **Language**: C17 (ISO/IEC 9899:2018), C23 features where supported
|
|
12
|
+
- **Compilers**: GCC 13+ / Clang 17+ (both required to compile cleanly)
|
|
13
|
+
- **Build System**: CMake 3.25+ (modern target-based)
|
|
14
|
+
- **Package Management**: vcpkg, Conan 2, or vendored dependencies
|
|
15
|
+
- **Formatting**: clang-format 17+
|
|
16
|
+
- **Static Analysis**: clang-tidy, cppcheck, Coverity (CI)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Key Libraries
|
|
21
|
+
|
|
22
|
+
### Core Utilities
|
|
23
|
+
- **libuv**: Cross-platform async I/O (event loop, TCP, UDP, pipes, timers)
|
|
24
|
+
- **jansson** or **cJSON**: JSON parsing and generation
|
|
25
|
+
- **libcurl**: HTTP client with TLS support
|
|
26
|
+
- **zlib**: Compression
|
|
27
|
+
|
|
28
|
+
### Data Structures & Algorithms
|
|
29
|
+
- **uthash**: Hash table macros (header-only)
|
|
30
|
+
- **sds**: Simple Dynamic Strings (Redis-derived)
|
|
31
|
+
- **stb**: Single-header libraries (image, truetype, etc.)
|
|
32
|
+
|
|
33
|
+
### Logging & Diagnostics
|
|
34
|
+
- **log.c**: Simple C logging library (or custom structured logger)
|
|
35
|
+
- **libbacktrace**: Stack trace generation for crash reporting
|
|
36
|
+
|
|
37
|
+
### Cryptography & Security
|
|
38
|
+
- **OpenSSL** / **mbedTLS**: TLS and cryptographic operations
|
|
39
|
+
- **libsodium**: Modern, easy-to-use crypto primitives
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Development Standards
|
|
44
|
+
|
|
45
|
+
### Code Quality
|
|
46
|
+
- **clang-format**: Automated formatting (`.clang-format` in repo root)
|
|
47
|
+
- **clang-tidy**: Lint and modernize checks
|
|
48
|
+
- **cppcheck**: Additional static analysis (finds what clang-tidy misses)
|
|
49
|
+
- **pre-commit hooks**: Run format and lint before each commit
|
|
50
|
+
|
|
51
|
+
### Security & Safety
|
|
52
|
+
- **AddressSanitizer (ASan)**: Buffer overflows, use-after-free, memory leaks
|
|
53
|
+
- **MemorySanitizer (MSan)**: Uninitialized memory reads (Clang only)
|
|
54
|
+
- **UndefinedBehaviorSanitizer (UBSan)**: Integer overflow, null deref, alignment
|
|
55
|
+
- **ThreadSanitizer (TSan)**: Data races in multithreaded code
|
|
56
|
+
- **Valgrind**: Heap profiling and leak detection (when sanitizers unavailable)
|
|
57
|
+
|
|
58
|
+
### Testing
|
|
59
|
+
- **Unity**: Lightweight unit test framework (single .c/.h)
|
|
60
|
+
- **CMock**: Mock generation for Unity (parses headers)
|
|
61
|
+
- **CMocka 2.0**: Alternative with built-in mocking and TAP output
|
|
62
|
+
- **AFL++ / libFuzzer**: Fuzz testing for parsing and input handling
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Development Environment
|
|
67
|
+
|
|
68
|
+
### Required Tools
|
|
69
|
+
- GCC 13+ or Clang 17+
|
|
70
|
+
- CMake 3.25+
|
|
71
|
+
- Make or Ninja
|
|
72
|
+
- clang-format, clang-tidy
|
|
73
|
+
- Valgrind (Linux) or Leaks (macOS)
|
|
74
|
+
- gdb or lldb
|
|
75
|
+
|
|
76
|
+
### Common Commands
|
|
77
|
+
```bash
|
|
78
|
+
# Build
|
|
79
|
+
cmake -B build -DCMAKE_BUILD_TYPE=Debug
|
|
80
|
+
cmake --build build
|
|
81
|
+
|
|
82
|
+
# Build with sanitizers
|
|
83
|
+
cmake -B build-asan -DCMAKE_BUILD_TYPE=Debug \
|
|
84
|
+
-DCMAKE_C_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer"
|
|
85
|
+
cmake --build build-asan
|
|
86
|
+
|
|
87
|
+
# Tests
|
|
88
|
+
ctest --test-dir build --output-on-failure
|
|
89
|
+
|
|
90
|
+
# Code quality
|
|
91
|
+
clang-format -i src/**/*.c include/**/*.h
|
|
92
|
+
clang-tidy src/*.c -- -Iinclude
|
|
93
|
+
cppcheck --enable=all --suppress=missingInclude src/
|
|
94
|
+
|
|
95
|
+
# Valgrind
|
|
96
|
+
valgrind --leak-check=full --show-leak-kinds=all ./build/myapp
|
|
97
|
+
|
|
98
|
+
# Coverage
|
|
99
|
+
cmake -B build-cov -DCMAKE_BUILD_TYPE=Debug \
|
|
100
|
+
-DCMAKE_C_FLAGS="--coverage"
|
|
101
|
+
cmake --build build-cov
|
|
102
|
+
ctest --test-dir build-cov
|
|
103
|
+
gcovr --root . --html --html-details -o coverage/index.html
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Key Technical Decisions
|
|
109
|
+
|
|
110
|
+
| Decision | Rationale |
|
|
111
|
+
|----------|-----------|
|
|
112
|
+
| **C17 over C11** | Bug fixes and clarifications; wide compiler support; stable baseline |
|
|
113
|
+
| **CMake over Make** | Target-based dependency management, cross-platform, IDE integration |
|
|
114
|
+
| **Unity over Check** | Minimal footprint, no library dependency, embedded-friendly |
|
|
115
|
+
| **clang-format + clang-tidy** | Single toolchain for formatting and linting, excellent C support |
|
|
116
|
+
| **Sanitizers over Valgrind-only** | Faster execution, compile-time instrumentation, CI-friendly |
|
|
117
|
+
| **vcpkg/Conan over manual vendoring** | Reproducible builds, version pinning, transitive dependency resolution |
|
|
118
|
+
| **C23 opt-in, not default** | Compiler support still maturing; adopt features like nullptr, auto, constexpr incrementally |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
_Document standards and patterns, not every dependency. See coding-style.md for detailed C conventions._
|