# Extended 3.2 — FileAttributesPlugin: readlink off-by-one stack overflow

Bug ref      : pharo.md §3.2
Severity     : HIGH (one-byte stack write of NUL — frame pointer / canary corruption)
File         : extracted/plugins/FileAttributesPlugin/src/unix/faSupport.c
Lines (HEAD) : 480-483

## Problem

```c
char targetFile[FA_PATH_MAX];
...
status = readlink(faGetPlatPath(aFaPath), targetFile, FA_PATH_MAX);
if (status >= 0) {
    targetFile[status] = 0;     // when status == FA_PATH_MAX → OOB write
```

`readlink(2)` returns the number of bytes written without a NUL.
When the symlink target length equals `FA_PATH_MAX`, `status ==
FA_PATH_MAX` and `targetFile[status]` writes one byte past the
stack buffer.

## Fix

Limit `readlink` to one less than the buffer size, leaving room
for the trailing NUL.

```diff
diff --git a/plugins/FileAttributesPlugin/src/unix/faSupport.c b/plugins/FileAttributesPlugin/src/unix/faSupport.c
index c415001aa..ea9447e0b 100644
--- a/plugins/FileAttributesPlugin/src/unix/faSupport.c
+++ b/plugins/FileAttributesPlugin/src/unix/faSupport.c
@@ -477,7 +477,9 @@ char		targetFile[FA_PATH_MAX];
 			return FA_CANT_STAT_PATH;
 		if (S_ISLNK(statBuf.st_mode)) {
 			/* This is a symbolic link, provide the target filename */
-			status = readlink(faGetPlatPath(aFaPath), targetFile, FA_PATH_MAX);
+			/* readlink does not NUL-terminate; leave one byte for the
+			 * trailing NUL so targetFile[status] = 0 cannot overflow. */
+			status = readlink(faGetPlatPath(aFaPath), targetFile, FA_PATH_MAX - 1);
 			if (status >= 0) {
 				targetFile[status] = 0;
 				targetOop = pathNameToOop(targetFile); } } }

```

## Test plan

- Create a symlink whose target is exactly FA_PATH_MAX bytes long;
  call lstat on it. Before: ASAN reports `stack-buffer-overflow` at
  the NUL write. After: clean.
- Normal short symlinks: unchanged behavior.

## Risk notes

- Truncates targets that are exactly FA_PATH_MAX bytes long to
  FA_PATH_MAX-1 bytes — that's the canonical safe pattern for
  readlink and matches what glibc's `realpath` does internally.
