Skip to content

Instantly share code, notes, and snippets.

@maxdeliso
Created February 21, 2012 04:55
Show Gist options
  • Save maxdeliso/1873806 to your computer and use it in GitHub Desktop.
Save maxdeliso/1873806 to your computer and use it in GitHub Desktop.
environ_flood.c
*~ *.swp eflood
eflood
======
This program repeatedly writes environment variables of the form 00000=XXXXX,
00001=XXXXX, ... using a call to setenv until the call fails or until the
maximum amount of bytes has been written to the environment. Both the length
of the name string, which is always padded by zeroes (and therefore always
the same length), and the length of the value string, which is always a
series of 'X's, must be specified as arguments on the command line. Before
each call to setenv the value of the environ pointer is saved, and afterwards
it is checked to see whether it had to change. The maximum number of bytes
that will be written to the environment is computed beforehand, using the
following formula:
max = 10 ^ nameSize * ( nameSize + valSize + 1)
If the calculation does not cause overflow then the program will terminate
when the maximum amount of bytes have been written. Otherwise, the program
will loop infinitely.
The purpose of the program is to investigate the semantics of setenv as
implemented by libc, and to see how it handles a growing environment. Here
is a sample run on a 64 bit machine running mac os x.
max bytes for sizes 4, 4: 90000
[ n : written ] environ: old environ -> new environ
[ 0 : 9 ] environ: 0x7fff5fbff4c0 -> 0x100200010
[ 1 : 18 ] environ: 0x100200010 -> 0x100200120
[ 3 : 36 ] environ: 0x100200120 -> 0x100200230
[ 5 : 54 ] environ: 0x100200230 -> 0x100200350
[ 7 : 72 ] environ: 0x100200350 -> 0x100200120
[ 19 : 180 ] environ: 0x100200120 -> 0x1002002d0
[ 21 : 198 ] environ: 0x1002002d0 -> 0x100200480
[ 23 : 216 ] environ: 0x100200480 -> 0x100200630
[ 25 : 234 ] environ: 0x100200630 -> 0x1002007f0
[ 27 : 252 ] environ: 0x1002007f0 -> 0x100200480
[ 73 : 666 ] environ: 0x100200480 -> 0x1002009c0
[ 75 : 684 ] environ: 0x1002009c0 -> 0x100200d10
[ 77 : 702 ] environ: 0x100200d10 -> 0x100200800
[ 93 : 846 ] environ: 0x100200800 -> 0x100200be0
[ 95 : 864 ] environ: 0x100200be0 -> 0x101000000
[ 97 : 882 ] environ: 0x101000000 -> 0x101000400
[ 161 : 1458 ] environ: 0x101000400 -> 0x101000a00
[ 225 : 2034 ] environ: 0x101000a00 -> 0x101001200
[ 289 : 2610 ] environ: 0x101001200 -> 0x101001c00
[ 353 : 3186 ] environ: 0x101001c00 -> 0x101000000
[ 865 : 7794 ] environ: 0x101000000 -> 0x101002800
[ 929 : 8370 ] environ: 0x101002800 -> 0x101004600
[ 993 : 8946 ] environ: 0x101004600 -> 0x101000000
[ 2209 : 19890 ] environ: 0x101000000 -> 0x101006600
[ 2273 : 20466 ] environ: 0x101006600 -> 0x10100ae00
[ 2337 : 21042 ] environ: 0x10100ae00 -> 0x101000000
[ 5537 : 49842 ] environ: 0x101000000 -> 0x10100f800
[ 5601 : 50418 ] environ: 0x10100f800 -> 0x10101a800
[ 5665 : 50994 ] environ: 0x10101a800 -> 0x101000000
/*
* Author: Max DeLiso <maxdeliso@gmail.com>
* Purpose: To investigate the semantics of setenv.
* Date: 2/20/12
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdbool.h>
#include <math.h>
#define BUILDER_MAX 128
extern char **environ;
const char VAL_CHAR = 'X';
int
main (int argc, char **argv)
{
unsigned long n;
unsigned long envNameSize;
unsigned long envValSize;
unsigned long envWritten;
unsigned long maxEnvWritten;
char *envNameBuf;
char *envValBuf;
char **lastEnviron;
char builderStr[BUILDER_MAX];
if (argc != 3)
{
fprintf (stderr,
"usage: %s [name length] [value length]\nhint: try %s 4 4\n",
argv[0], argv[0]);
exit (1);
}
envNameSize = strtoul (argv[1], NULL, 10);
envValSize = strtoul (argv[2], NULL, 10);
#ifndef NDEBUG
fprintf (stderr, "nameSize: %lu valSize: %lu\n", envNameSize, envValSize);
#endif
/*
* Allocate space for both buffers, and fill the envValBuf with VAL_CHAR
*/
envNameBuf = malloc (envNameSize + 1);
envValBuf = malloc (envValSize + 1);
if (!envNameBuf || !envValBuf)
{
perror ("malloc");
exit (2);
}
memset (envValBuf, VAL_CHAR, envValSize);
/*
* Here we build the string which we will use in each call to snprintf in
* the main loop, which we want to take the form of '%0[number]lu'. We
* then pass n to snprintf using this string we've built as our format
* string, which will always be envNameBytes long due to the fact that
* the leading 0 is part of the format string argument. We only need to
* do this once.
*/
(void) snprintf (builderStr, sizeof (builderStr), "%%0%lulu", envNameSize);
#ifndef NDEBUG
fprintf (stderr, "builderStr: %s\n", builderStr);
#endif
/*
* Here we compute the maximum amount of bytes we will write given the
* lengths we've specified for the environment variable name and size,
* and check for overflow.
*/
maxEnvWritten =
pow (10.0f, (float) envNameSize) * (envValSize + envNameSize + 1);
if (maxEnvWritten != 0)
{
printf ("max bytes for sizes %lu, %lu: %lu\n", envValSize,
envNameSize, maxEnvWritten);
}
else
{
printf ("max size overflowed... defaulting to infinite loop\n");
}
printf ("[%16s : %-16s] environ: %16s -> %-16s\n\n",
"n", "written", "old environ", "new environ");
for (n = 0, envWritten = 0;
(maxEnvWritten != 0) ? (envWritten < maxEnvWritten) : (true); ++n)
{
(void) snprintf (envNameBuf, envNameSize + 1, builderStr, n);
lastEnviron = environ;
#ifndef NDEBUG
fprintf (stderr, "name: %s\n val: %s\n n: %lu\n\n", envNameBuf,
envValBuf, n);
#endif
if (setenv (envNameBuf, envValBuf, 1) != 0)
{
perror ("setenv");
exit (3);
}
envWritten += (envNameSize + envValSize + 1);
if (environ != lastEnviron)
{
printf ("[%16lu : %-16lu] environ: %16p -> %-16p\n",
n, envWritten, (void *) lastEnviron, (void *) environ);
}
}
free (envValBuf);
free (envNameBuf);
return 0;
}
## Max DeLiso <maxdeliso@gmail.com>
.PHONY: clean
CC=gcc
CFLAGS=-std=c99 -Wall -Wextra -pedantic -O3 -lm -D_POSIX_C_SOURCE=200112L
RFLAGS=-DNDEBUG
DFLAGS=-g
OUT=eflood
RM=rm
STRIP=strip
SOURCE=eflood.c
INSTALL=install
INSTALLDIR=/usr/local/bin
$(OUT): $(SOURCE)
@$(CC) $(CFLAGS) $(RFLAGS) $(SOURCE) -o $(OUT)
@$(STRIP) $(OUT)
debug: $(SOURCE)
@$(CC) $(CFLAGS) $(DFLAGS) $(SOURCE) -o $(OUT)
clean:
@$(RM) -f $(OUT)
install: $(OUT)
@$(INSTALL) $(OUT) $(INSTALLDIR)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment