Skip to content

Instantly share code, notes, and snippets.

@nuew
Created August 8, 2023 01:52
Show Gist options
  • Save nuew/e1f1bdb6bb37073a93d44e3a9554e3b8 to your computer and use it in GitHub Desktop.
Save nuew/e1f1bdb6bb37073a93d44e3a9554e3b8 to your computer and use it in GitHub Desktop.
somehow, i have made use of `gets` safely
// run with e.g. `tr -cd '\n -~' < /dev/urandom | head -n 10000 | cat /dev/stdin \
// <(for f in /usr/bin/*; do printf $f; done) | ./safe_gets 0x1000`
#include <features.h>
#ifdef __GLIBC__
#undef __GLIBC_USE_DEPRECATED_GETS
#define __GLIBC_USE_DEPRECATED_GETS 1
#else
#undef __STDC_VERSION__
#define __STDC_VERSION__ 199901L
#endif
#include <errno.h>
#include <signal.h>
#include <setjmp.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
static jmp_buf gets_reset;
static char *allocate_gets_buffer(size_t *size) {
// get page size and page offset mask
size_t page_size = sysconf(_SC_PAGE_SIZE);
size_t page_mask = page_size - 1;
size_t max_size = SIZE_MAX - (page_size << 1);
// check size is not too big and round up to nearest page and add an extra page to block off
if(*size > max_size) {
printf("buffer size of 0x%zx bytes too big; "
"buffer must be no more than 0x%zx bytes\n", *size, max_size);
return NULL;
}
*size = (*size + (page_mask << 1)) & (~page_mask);
// allocate buffer
char *buf = mmap(0, *size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(buf == MAP_FAILED) {
perror("mmap");
return NULL;
}
// make access to the final page invalid
if(mprotect(buf + *size - page_size, page_size, PROT_NONE)) {
perror("mprotect");
munmap(buf, *size); // shouldn't be able to fail with these arguments
return NULL;
};
return buf;
}
static void signal_handler(int signal, siginfo_t *info, void *context) {
longjmp(gets_reset, 1);
}
int main(int argc, char **argv) {
// make sure we have exactly two arguments
if(argc != 2) {
puts("usage: ./safe_gets <buffer length>");
return EXIT_FAILURE;
}
// get buffer size from argument
errno = 0;
size_t buffer_size = strtoul(argv[1], NULL, 0);
if(errno != 0) {
perror("strtoul");
return EXIT_FAILURE;
}
// setup signal set
sigset_t sigset;
sigfillset(&sigset);
// setup signals
const struct sigaction sa = {
.sa_sigaction = signal_handler,
.sa_mask = sigset,
.sa_flags = SA_RESTART | SA_SIGINFO,
};
if(sigaction(SIGSEGV, &sa, NULL) || sigaction(SIGBUS, &sa, NULL)) {
perror("sigaction");
return EXIT_FAILURE;
}
// allocate safe gets buffer
char *buf = allocate_gets_buffer(&buffer_size);
if(buf == NULL) return EXIT_FAILURE;
while(1) {
if(setjmp(gets_reset)) {
puts("overflowed buffer");
break;
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
if(!gets(buf)) {
#pragma GCC diagnostic pop
if(errno != 0) {
perror("gets");
return EXIT_FAILURE;
} else break;
}
if(puts(buf) == EOF) {
perror("puts");
return EXIT_FAILURE;
}
}
munmap(buf, buffer_size);
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment