Created
August 8, 2023 01:52
-
-
Save nuew/e1f1bdb6bb37073a93d44e3a9554e3b8 to your computer and use it in GitHub Desktop.
somehow, i have made use of `gets` safely
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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