Skip to content

Commit 5f1a540

Browse files
committed
[CommandLine][Linux] Don't assume the stack is one range in maps.
In `/proc/self/maps`, the stack isn't necessarily a single range, even immediately after process start-up. Apparently sometimes it's possible for the kernel to allocate extra ranges below the first one, and these show up separately in `/proc/self/maps`. The upshot was that we were finding that `environ` didn't point into the stack range we found, and then returning no arguments as a result. The fix is to merge contiguous ranges after the first stack range we find in `/proc/self/maps`. rdar://117963394
1 parent ff1cfbd commit 5f1a540

File tree

1 file changed

+64
-11
lines changed

1 file changed

+64
-11
lines changed

stdlib/public/CommandLineSupport/CommandLine.cpp

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,14 @@ static void swift::enumerateUnsafeArgv(const F& body) { }
221221
// we don't need to take advantage of that here and can stick to things that
222222
// are defined in the ABI specs.)
223223

224-
// We'll need this in a minute
225-
extern char **environ;
224+
#include <unistd.h>
225+
226+
#define DEBUG_ARGVGRABBER 0
227+
#if DEBUG_ARGVGRABBER
228+
#define ARGVDEBUG(...) fprintf(stderr, __VA_ARGS__)
229+
#else
230+
#define ARGVDEBUG(...)
231+
#endif
226232

227233
namespace {
228234

@@ -248,8 +254,10 @@ struct ArgvGrabber {
248254
// Find the stack by looking at /proc/self/maps
249255
ArgvGrabber::stack ArgvGrabber::findStack(void) {
250256
FILE *maps = fopen("/proc/self/maps", "r");
251-
if (!maps)
257+
if (!maps) {
258+
ARGVDEBUG("unable to open maps - %d\n", errno);
252259
return stack();
260+
}
253261

254262
char line[256];
255263
void *base = NULL, *top = NULL;
@@ -260,34 +268,69 @@ ArgvGrabber::stack ArgvGrabber::findStack(void) {
260268
//
261269
// Note that we can't look for [stack], because Rosetta and qemu
262270
// set up a separate stack for the emulated code.
263-
if (sscanf(line, "%p-%p", &base, &top) == 2
264-
&& (void *)line >= base && (void *)line < top) {
265-
found = true;
266-
break;
271+
//
272+
// We also need to glom on extra VM ranges after the first one
273+
// we find, because *sometimes* we end up with an extra range.
274+
void *lo, *hi;
275+
if (sscanf(line, "%p-%p", &lo, &hi) == 2) {
276+
if ((void *)line >= lo && (void *)line < hi) {
277+
base = lo;
278+
top = hi;
279+
found = true;
280+
} else if (found && top == lo) {
281+
top = hi;
282+
}
267283
}
268284
}
269285

270286
fclose(maps);
271287

272-
if (!found)
288+
if (!found) {
289+
ARGVDEBUG("stack not found in maps\n");
273290
return stack();
291+
}
274292

275293
return stack(base, top);
276294
}
277295

296+
#if DEBUG_ARGVGRABBER
297+
void printMaps() {
298+
FILE *maps = fopen("/proc/self/maps", "r");
299+
if (!maps) {
300+
fprintf(stderr, "unable to open maps - %d\n", errno);
301+
return;
302+
}
303+
304+
char line[256];
305+
while (fgets(line, sizeof(line), maps)) {
306+
fputs(line, stderr);
307+
}
308+
309+
fclose(maps);
310+
}
311+
#endif
312+
278313
// Find argv by walking backwards from environ
279314
void ArgvGrabber::findArgv(ArgvGrabber::stack stack) {
280-
if (!stack.base)
315+
if (!stack.base) {
316+
ARGVDEBUG("no stack\n");
281317
return;
318+
}
282319

283320
// Check that environ points to the stack
284321
char **envp = environ;
285-
if ((void *)envp < stack.base || (void *)envp >= stack.top)
322+
if ((void *)envp < stack.base || (void *)envp >= stack.top) {
323+
ARGVDEBUG("envp = %p, stack is from %p to %p\n",
324+
envp, stack.base, stack.top);
325+
#if DEBUG_ARGVGRABBER
326+
printMaps();
327+
#endif
286328
return;
329+
}
287330

288331
char **ptr = envp - 1;
289332

290-
// We're now pointing at the NULL that terminates argv. Keep going back
333+
// We're now pointing at the NULL that terminates argv. Keep going back
291334
// while we're seeing pointers (values greater than envp).
292335
while ((void *)(ptr - 1) > stack.base) {
293336
--ptr;
@@ -299,10 +342,20 @@ void ArgvGrabber::findArgv(ArgvGrabber::stack stack) {
299342
return;
300343
}
301344
}
345+
346+
ARGVDEBUG("didn't find argc\n");
302347
}
303348

304349
ArgvGrabber::ArgvGrabber() : argv(nullptr), argc(0) {
350+
ARGVDEBUG("***GRABBING ARGV for %d***\n", getpid());
305351
findArgv(findStack());
352+
#if DEBUG_ARGVGRABBER
353+
fprintf(stderr, "ARGV is at %p with count %d\n", argv, argc);
354+
for (int i = 0; i < argc; ++i) {
355+
fprintf(stderr, " argv[%d] = \"%s\"\n", i, argv[i]);
356+
}
357+
fprintf(stderr, "***ARGV GRABBED***\n");
358+
#endif
306359
}
307360

308361
ArgvGrabber argvGrabber;

0 commit comments

Comments
 (0)