From 5c9e7e74ee7ad9e8b68b3345a41256eab83a18cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20R=C3=BChsen?= Date: Tue, 13 Feb 2018 15:41:58 +0100 Subject: [PATCH] Limit CPU wasting on large inputs Large inputs on psl_registrable_domain() and psl_unregistrable_domain() suffer from a O(N^2) behavior. This change limits N to avoid excessive CPU usage. At the same time we limit the fuzz corpora size to 64k which is far more then we expect any real life domain to be. Reported-by: OSS-Fuzz --- fuzz/libpsl_fuzzer.c | 6 +++++- src/psl.c | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/fuzz/libpsl_fuzzer.c b/fuzz/libpsl_fuzzer.c index ef6a6ef..786da3d 100644 --- a/fuzz/libpsl_fuzzer.c +++ b/fuzz/libpsl_fuzzer.c @@ -40,9 +40,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { static int first_run = 1; psl_ctx_t *psl; - char *domain = (char *) malloc(size + 1), *res; + char *domain, *res; int rc; + if (size > 64 * 1024 - 1) + return 0; + + domain = (char *) malloc(size + 1); assert(domain != NULL); /* 0 terminate */ diff --git a/src/psl.c b/src/psl.c index 1691d5c..323409c 100644 --- a/src/psl.c +++ b/src/psl.c @@ -1048,6 +1048,19 @@ const char *psl_unregistrable_domain(const psl_ctx_t *psl, const char *domain) if (!psl || !domain) return NULL; + /* + * In the main loop we introduce a O(N^2) behavior to avoid code duplication. + * To avoid nasty CPU hogging, we limit the lookup to max. 8 domain labels to the right. + */ + + int nlabels = 0; + for (const char *p = domain + strlen(domain) - 1; p >= domain; p--) { + if (*p == '.' && ++nlabels > 8) { + domain = p + 1; + break; + } + } + /* * We check from left to right to catch special PSL entries like 'forgot.his.name': * 'forgot.his.name' and 'name' are in the PSL while 'his.name' is not. @@ -1090,6 +1103,19 @@ const char *psl_registrable_domain(const psl_ctx_t *psl, const char *domain) if (!psl || !domain || *domain == '.') return NULL; + /* + * In the main loop we introduce a O(N^2) behavior to avoid code duplication. + * To avoid nasty CPU hogging, we limit the lookup to max. 8 domain labels to the right. + */ + + int nlabels = 0; + for (p = domain + strlen(domain) - 1; p >= domain; p--) { + if (*p == '.' && ++nlabels > 8) { + domain = p + 1; + break; + } + } + /* * We check from left to right to catch special PSL entries like 'forgot.his.name': * 'forgot.his.name' and 'name' are in the PSL while 'his.name' is not.