CVE-2022-29967: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22)
value: static_compressed_inmemory_website_callback.c in Glewlwyd through 2.6.2 allows directory traversal.
CWE description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
CVSS v3 base score: 7.5
Published: 2022-04-29T23:15Z
Repository: https://github.com/babelouest/glewlwyd
Language: C
Files changed: src/static_compressed_inmemory_website_callback.c, src/static_compressed_inmemory_website_callback.h
Commit date: 2022-04-29 07:26:44 -0400
Commit message: Fix file access check for directory traversal, and fix call for callback_static_file_uncompressed if header not set

Diff: diff --git a/src/static_compressed_inmemory_website_callback.c b/src/static_compressed_inmemory_website_callback.c
index 64c8d809a..78189a319 100644
--- a/src/static_compressed_inmemory_website_callback.c
+++ b/src/static_compressed_inmemory_website_callback.c
@@ -2,11 +2,11 @@
  *
  * Static file server with compression Ulfius callback
  *
  * Copyright 2020-2022 Nicolas Mora <mail@babelouest.org>
  *
- * Version 20220425
+ * Version 20220428
  *
  * The MIT License (MIT)
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -87,10 +87,12 @@
  * }
  *
  */
 #include <pthread.h>
 #include <zlib.h>
+#include <limits.h>
+#include <stdlib.h>
 #include <string.h>
 #include <ulfius.h>
 
 #include "static_compressed_inmemory_website_callback.h"
 
@@ -156,11 +158,11 @@ static void callback_static_file_uncompressed_stream_free(void * cls) {
  * static file callback endpoint
  */
 static int callback_static_file_uncompressed (const struct _u_request * request, struct _u_response * response, void * user_data) {
   size_t length;
   FILE * f;
-  char * file_requested, * file_path, * url_dup_save;
+  char * file_requested, * file_path, * url_dup_save, * real_path = NULL;
   const char * content_type;
   int ret = U_CALLBACK_CONTINUE;
 
   if (user_data != NULL && ((struct _u_compressed_inmemory_website_config *)user_data)->files_path != NULL) {
     file_requested = o_strdup(request->http_url);
@@ -183,38 +185,44 @@ static int callback_static_file_uncompressed (const struct _u_request * request,
       o_free(url_dup_save);
       url_dup_save = file_requested = o_strdup("index.html");
     }
 
     file_path = msprintf("%s/%s", ((struct _u_compressed_inmemory_website_config *)user_data)->files_path, file_requested);
+    real_path = realpath(file_path, NULL);
+    if (0 == o_strncmp(((struct _u_compressed_inmemory_website_config *)user_data)->files_path, real_path, o_strlen(((struct _u_compressed_inmemory_website_config *)user_data)->files_path))) {
+      f = fopen (file_path, "rb");
+      if (f) {
+        fseek (f, 0, SEEK_END);
+        length = ftell (f);
+        fseek (f, 0, SEEK_SET);
+
+        content_type = u_map_get_case(&((struct _u_compressed_inmemory_website_config *)user_data)->mime_types, get_filename_ext(file_requested));
+        if (content_type == NULL) {
+          content_type = u_map_get(&((struct _u_compressed_inmemory_website_config *)user_data)->mime_types, "*");
+          y_log_message(Y_LOG_LEVEL_WARNING, "Static File Server - Unknown mime type for extension %s", get_filename_ext(file_requested));
+        }
+        u_map_put(response->map_header, "Content-Type", content_type);
+        u_map_copy_into(response->map_header, &((struct _u_compressed_inmemory_website_config *)user_data)->map_header);
 
-    f = fopen (file_path, "rb");
-    if (f) {
-      fseek (f, 0, SEEK_END);
-      length = ftell (f);
-      fseek (f, 0, SEEK_SET);
-
-      content_type = u_map_get_case(&((struct _u_compressed_inmemory_website_config *)user_data)->mime_types, get_filename_ext(file_requested));
-      if (content_type == NULL) {
-        content_type = u_map_get(&((struct _u_compressed_inmemory_website_config *)user_data)->mime_types, "*");
-        y_log_message(Y_LOG_LEVEL_WARNING, "Static File Server - Unknown mime type for extension %s", get_filename_ext(file_requested));
-      }
-      u_map_put(response->map_header, "Content-Type", content_type);
-      u_map_copy_into(response->map_header, &((struct _u_compressed_inmemory_website_config *)user_data)->map_header);
-
-      if (ulfius_set_stream_response(response, 200, callback_static_file_uncompressed_stream, callback_static_file_uncompressed_stream_free, length, CHUNK, f) != U_OK) {
-        y_log_message(Y_LOG_LEVEL_ERROR, "Static File Server - Error ulfius_set_stream_response");
-      }
-    } else {
-      if (((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404 == NULL) {
-        ret = U_CALLBACK_IGNORE;
+        if (ulfius_set_stream_response(response, 200, callback_static_file_uncompressed_stream, callback_static_file_uncompressed_stream_free, length, CHUNK, f) != U_OK) {
+          y_log_message(Y_LOG_LEVEL_ERROR, "Static File Server - Error ulfius_set_stream_response");
+        }
       } else {
-        ulfius_add_header_to_response(response, "Location", ((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404);
-        response->status = 302;
+        if (((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404 == NULL) {
+          ret = U_CALLBACK_IGNORE;
+        } else {
+          ulfius_add_header_to_response(response, "Location", ((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404);
+          response->status = 302;
+        }
       }
+      o_free(url_dup_save);
+    } else {
+      response->status = 403;
     }
     o_free(file_path);
-    o_free(url_dup_save);
+    free(real_path); // realpath uses malloc
+
   } else {
     y_log_message(Y_LOG_LEVEL_ERROR, "Static File Server - Error, user_data is NULL or inconsistent");
     ret = U_CALLBACK_ERROR;
   }
   return ret;
@@ -288,11 +296,11 @@ int callback_static_compressed_inmemory_website (const struct _u_request * reque
   int ret = U_CALLBACK_CONTINUE, compress_mode = U_COMPRESS_NONE, res;
   z_stream defstream;
   unsigned char * file_content, * file_content_orig = NULL;
   size_t length, read_length, offset, data_zip_len = 0;
   FILE * f;
-  char * file_requested, * file_path, * url_dup_save, * data_zip = NULL;
+  char * file_requested, * file_path, * url_dup_save, * data_zip = NULL, * real_path = NULL;
   const char * content_type;
 
   /*
    * Comment this if statement if you don't access static files url from root dir, like /app
    */
@@ -326,148 +334,156 @@ int callback_static_compressed_inmemory_website (const struct _u_request * reque
           compress_mode = U_COMPRESS_GZIP;
         } else if (config->allow_deflate && string_array_has_trimmed_value((const char **)accept_list, U_ACCEPT_DEFLATE)) {
           compress_mode = U_COMPRESS_DEFL;
         }
 
-
         if (compress_mode != U_COMPRESS_NONE) {
           if (compress_mode == U_COMPRESS_GZIP && config->allow_cache_compressed && u_map_has_key(&config->gzip_files, file_requested)) {
             ulfius_set_binary_body_response(response, 200, u_map_get(&config->gzip_files, file_requested), u_map_get_length(&config->gzip_files, file_requested));
             u_map_put(response->map_header, U_CONTENT_HEADER, U_ACCEPT_GZIP);
-            
+
             content_type = u_map_get_case(&config->mime_types, get_filename_ext(file_requested));
             if (content_type == NULL) {
               content_type = u_map_get(&config->mime_types, "*");
             }
             u_map_put(response->map_header, "Content-Type", content_type);
             u_map_copy_into(response->map_header, &config->map_header);
           } else if (compress_mode == U_COMPRESS_DEFL && config->allow_cache_compressed && u_map_has_key(&config->deflate_files, file_requested)) {
             ulfius_set_binary_body_response(response, 200, u_map_get(&config->deflate_files, file_requested), u_map_get_length(&config->deflate_files, file_requested));
             u_map_put(response->map_header, U_CONTENT_HEADER, U_ACCEPT_DEFLATE);
-            
+
             content_type = u_map_get_case(&config->mime_types, get_filename_ext(file_requested));
             if (content_type == NULL) {
               content_type = u_map_get(&config->mime_types, "*");
             }
             u_map_put(response->map_header, "Content-Type", content_type);
             u_map_copy_into(response->map_header, &config->map_header);
           } else {
             file_path = msprintf("%s/%s", ((struct _u_compressed_inmemory_website_config *)user_data)->files_path, file_requested);
+            real_path = realpath(file_path, NULL);
+            if (0 == o_strncmp(((struct _u_compressed_inmemory_website_config *)user_data)->files_path, real_path, o_strlen(((struct _u_compressed_inmemory_website_config *)user_data)->files_path))) {
+              if (!pthread_mutex_lock(&config->lock)) {
+                f = fopen (file_path, "rb");
+                if (f) {
+                  content_type = u_map_get_case(&config->mime_types, get_filename_ext(file_requested));
+                  if (content_type == NULL) {
+                    content_type = u_map_get(&config->mime_types, "*");
+                    y_log_message(Y_LOG_LEVEL_WARNING, "Static File Server - Unknown mime type for extension %s", get_filename_ext(file_requested));
+                  }
+                  if (!string_array_has_value((const char **)config->mime_types_compressed, content_type)) {
+                    compress_mode = U_COMPRESS_NONE;
+                  }
 
-            if (!pthread_mutex_lock(&config->lock)) {
-              f = fopen (file_path, "rb");
-              if (f) {
-                content_type = u_map_get_case(&config->mime_types, get_filename_ext(file_requested));
-                if (content_type == NULL) {
-                  content_type = u_map_get(&config->mime_types, "*");
-                  y_log_message(Y_LOG_LEVEL_WARNING, "Static File Server - Unknown mime type for extension %s", get_filename_ext(file_requested));
-                }
-                if (!string_array_has_value((const char **)config->mime_types_compressed, content_type)) {
-                  compress_mode = U_COMPRESS_NONE;
-                }
-
-                u_map_put(response->map_header, "Content-Type", content_type);
-                u_map_copy_into(response->map_header, &config->map_header);
-                
-                fseek (f, 0, SEEK_END);
-                offset = length = ftell (f);
-                fseek (f, 0, SEEK_SET);
-
-                if (length) {
-                  if ((file_content_orig = file_content = o_malloc(length)) != NULL && (data_zip = o_malloc((2*length)+20)) != NULL) {
-                    defstream.zalloc = u_zalloc;
-                    defstream.zfree = u_zfree;
-                    defstream.opaque = Z_NULL;
-                    defstream.avail_in = (uInt)length;
-                    defstream.next_in = (Bytef *)file_content;
-                    while ((read_length = fread(file_content, sizeof(char), offset, f))) {
-                      file_content += read_length;
-                      offset -= read_length;
-                    }
-
-                    if (compress_mode == U_COMPRESS_GZIP) {
-                      if (deflateInit2(&defstream,
-                                       Z_DEFAULT_COMPRESSION,
-                                       Z_DEFLATED,
-                                       U_GZIP_WINDOW_BITS | U_GZIP_ENCODING,
-                                       8,
-                                       Z_DEFAULT_STRATEGY) != Z_OK) {
-                        y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflateInit (gzip)");
-                        ret = U_CALLBACK_ERROR;
-                      }
-                    } else {
-                      if (deflateInit(&defstream, Z_BEST_COMPRESSION) != Z_OK) {
-                        y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflateInit (deflate)");
-                        ret = U_CALLBACK_ERROR;
+                  u_map_put(response->map_header, "Content-Type", content_type);
+                  u_map_copy_into(response->map_header, &config->map_header);
+
+                  fseek (f, 0, SEEK_END);
+                  offset = length = ftell (f);
+                  fseek (f, 0, SEEK_SET);
+
+                  if (length) {
+                    if ((file_content_orig = file_content = o_malloc(length)) != NULL && (data_zip = o_malloc((2*length)+20)) != NULL) {
+                      defstream.zalloc = u_zalloc;
+                      defstream.zfree = u_zfree;
+                      defstream.opaque = Z_NULL;
+                      defstream.avail_in = (uInt)length;
+                      defstream.next_in = (Bytef *)file_content;
+                      while ((read_length = fread(file_content, sizeof(char), offset, f))) {
+                        file_content += read_length;
+                        offset -= read_length;
                       }
-                    }
-                    if (ret == U_CALLBACK_CONTINUE) {
-                      do {
-                        if ((data_zip = o_realloc(data_zip, data_zip_len+_U_W_BLOCK_SIZE)) != NULL) {
-                          defstream.avail_out = _U_W_BLOCK_SIZE;
-                          defstream.next_out = ((Bytef *)data_zip)+data_zip_len;
-                          switch ((res = deflate(&defstream, Z_FINISH))) {
-                            case Z_OK:
-                            case Z_STREAM_END:
-                            case Z_BUF_ERROR:
-                              break;
-                            default:
-                              y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflate %d", res);
-                              ret = U_CALLBACK_ERROR;
-                              break;
-                          }
-                          data_zip_len += _U_W_BLOCK_SIZE - defstream.avail_out;
-                        } else {
-                          y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error allocating resources for data_zip");
+
+                      if (compress_mode == U_COMPRESS_GZIP) {
+                        if (deflateInit2(&defstream,
+                                         Z_DEFAULT_COMPRESSION,
+                                         Z_DEFLATED,
+                                         U_GZIP_WINDOW_BITS | U_GZIP_ENCODING,
+                                         8,
+                                         Z_DEFAULT_STRATEGY) != Z_OK) {
+                          y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflateInit (gzip)");
                           ret = U_CALLBACK_ERROR;
                         }
-                      } while (U_CALLBACK_CONTINUE == ret && defstream.avail_out == 0);
-
+                      } else {
+                        if (deflateInit(&defstream, Z_BEST_COMPRESSION) != Z_OK) {
+                          y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflateInit (deflate)");
+                          ret = U_CALLBACK_ERROR;
+                        }
+                      }
                       if (ret == U_CALLBACK_CONTINUE) {
-                        if (compress_mode == U_COMPRESS_GZIP) {
-                          if (config->allow_cache_compressed) {
-                            u_map_put_binary(&config->gzip_files, file_requested, data_zip, 0, defstream.total_out);
+                        do {
+                          if ((data_zip = o_realloc(data_zip, data_zip_len+_U_W_BLOCK_SIZE)) != NULL) {
+                            defstream.avail_out = _U_W_BLOCK_SIZE;
+                            defstream.next_out = ((Bytef *)data_zip)+data_zip_len;
+                            switch ((res = deflate(&defstream, Z_FINISH))) {
+                              case Z_OK:
+                              case Z_STREAM_END:
+                              case Z_BUF_ERROR:
+                                break;
+                              default:
+                                y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflate %d", res);
+                                ret = U_CALLBACK_ERROR;
+                                break;
+                            }
+                            data_zip_len += _U_W_BLOCK_SIZE - defstream.avail_out;
+                          } else {
+                            y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error allocating resources for data_zip");
+                            ret = U_CALLBACK_ERROR;
                           }
-                          ulfius_set_binary_body_response(response, 200, u_map_get(&config->gzip_files, file_requested), u_map_get_length(&config->gzip_files, file_requested));
-                        } else {
-                          if (config->allow_cache_compressed) {
-                            u_map_put_binary(&config->deflate_files, file_requested, data_zip, 0, defstream.total_out);
+                        } while (U_CALLBACK_CONTINUE == ret && defstream.avail_out == 0);
+
+                        if (ret == U_CALLBACK_CONTINUE) {
+                          if (compress_mode == U_COMPRESS_GZIP) {
+                            if (config->allow_cache_compressed) {
+                              u_map_put_binary(&config->gzip_files, file_requested, data_zip, 0, defstream.total_out);
+                            }
+                            ulfius_set_binary_body_response(response, 200, u_map_get(&config->gzip_files, file_requested), u_map_get_length(&config->gzip_files, file_requested));
+                          } else {
+                            if (config->allow_cache_compressed) {
+                              u_map_put_binary(&config->deflate_files, file_requested, data_zip, 0, defstream.total_out);
+                            }
+                            ulfius_set_binary_body_response(response, 200, u_map_get(&config->deflate_files, file_requested), u_map_get_length(&config->deflate_files, file_requested));
                           }
-                          ulfius_set_binary_body_response(response, 200, u_map_get(&config->deflate_files, file_requested), u_map_get_length(&config->deflate_files, file_requested));
+                          u_map_put(response->map_header, U_CONTENT_HEADER, compress_mode==U_COMPRESS_GZIP?U_ACCEPT_GZIP:U_ACCEPT_DEFLATE);
                         }
-                        u_map_put(response->map_header, U_CONTENT_HEADER, compress_mode==U_COMPRESS_GZIP?U_ACCEPT_GZIP:U_ACCEPT_DEFLATE);
                       }
+                      deflateEnd(&defstream);
+                      o_free(data_zip);
+                    } else {
+                      y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error allocating resource for file_content or data_zip");
+                      ret = U_CALLBACK_ERROR;
                     }
-                    deflateEnd(&defstream);
-                    o_free(data_zip);
+                    o_free(file_content_orig);
+                  }
+                  fclose(f);
+                } else {
+                  if (((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404 == NULL) {
+                    ret = U_CALLBACK_IGNORE;
                   } else {
-                    y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error allocating resource for file_content or data_zip");
-                    ret = U_CALLBACK_ERROR;
+                    ulfius_add_header_to_response(response, "Location", ((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404);
+                    response->status = 302;
                   }
-                  o_free(file_content_orig);
                 }
-                fclose(f);
+                pthread_mutex_unlock(&config->lock);
               } else {
-                if (((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404 == NULL) {
-                  ret = U_CALLBACK_IGNORE;
-                } else {
-                  ulfius_add_header_to_response(response, "Location", ((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404);
-                  response->status = 302;
-                }
+                y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error pthread_lock_mutex");
+                ret = U_CALLBACK_ERROR;
               }
-              pthread_mutex_unlock(&config->lock);
             } else {
-              y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error pthread_lock_mutex");
-              ret = U_CALLBACK_ERROR;
+              response->status = 403;
             }
             o_free(file_path);
+            free(real_path); // realpath uses malloc
           }
         } else {
           ret = callback_static_file_uncompressed(request, response, user_data);
         }
         free_string_array(accept_list);
+      } else {
+        ret = callback_static_file_uncompressed(request, response, user_data);
       }
+    } else {
+      ret = callback_static_file_uncompressed(request, response, user_data);
     }
     o_free(url_dup_save);
   }
 
   return ret;
diff --git a/src/static_compressed_inmemory_website_callback.h b/src/static_compressed_inmemory_website_callback.h
index 1df9a7eba..ef27283fe 100644
--- a/src/static_compressed_inmemory_website_callback.h
+++ b/src/static_compressed_inmemory_website_callback.h
@@ -2,11 +2,11 @@
  *
  * Static file server with compression Ulfius callback
  *
  * Copyright 2020-2022 Nicolas Mora <mail@babelouest.org>
  *
- * Version 20220425
+ * Version 20220428
  * 
  * The MIT License (MIT)
  * 
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal

Vulnerable code:  * Version 20220425
  char * file_requested, * file_path, * url_dup_save;
    f = fopen (file_path, "rb");
    if (f) {
      fseek (f, 0, SEEK_END);
      length = ftell (f);
      fseek (f, 0, SEEK_SET);

      content_type = u_map_get_case(&((struct _u_compressed_inmemory_website_config *)user_data)->mime_types, get_filename_ext(file_requested));
      if (content_type == NULL) {
        content_type = u_map_get(&((struct _u_compressed_inmemory_website_config *)user_data)->mime_types, "*");
        y_log_message(Y_LOG_LEVEL_WARNING, "Static File Server - Unknown mime type for extension %s", get_filename_ext(file_requested));
      }
      u_map_put(response->map_header, "Content-Type", content_type);
      u_map_copy_into(response->map_header, &((struct _u_compressed_inmemory_website_config *)user_data)->map_header);

      if (ulfius_set_stream_response(response, 200, callback_static_file_uncompressed_stream, callback_static_file_uncompressed_stream_free, length, CHUNK, f) != U_OK) {
        y_log_message(Y_LOG_LEVEL_ERROR, "Static File Server - Error ulfius_set_stream_response");
      }
    } else {
      if (((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404 == NULL) {
        ret = U_CALLBACK_IGNORE;
        ulfius_add_header_to_response(response, "Location", ((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404);
        response->status = 302;
    o_free(url_dup_save);
  char * file_requested, * file_path, * url_dup_save, * data_zip = NULL;

            
            
            if (!pthread_mutex_lock(&config->lock)) {
              f = fopen (file_path, "rb");
              if (f) {
                content_type = u_map_get_case(&config->mime_types, get_filename_ext(file_requested));
                if (content_type == NULL) {
                  content_type = u_map_get(&config->mime_types, "*");
                  y_log_message(Y_LOG_LEVEL_WARNING, "Static File Server - Unknown mime type for extension %s", get_filename_ext(file_requested));
                }
                if (!string_array_has_value((const char **)config->mime_types_compressed, content_type)) {
                  compress_mode = U_COMPRESS_NONE;
                }

                u_map_put(response->map_header, "Content-Type", content_type);
                u_map_copy_into(response->map_header, &config->map_header);
                
                fseek (f, 0, SEEK_END);
                offset = length = ftell (f);
                fseek (f, 0, SEEK_SET);

                if (length) {
                  if ((file_content_orig = file_content = o_malloc(length)) != NULL && (data_zip = o_malloc((2*length)+20)) != NULL) {
                    defstream.zalloc = u_zalloc;
                    defstream.zfree = u_zfree;
                    defstream.opaque = Z_NULL;
                    defstream.avail_in = (uInt)length;
                    defstream.next_in = (Bytef *)file_content;
                    while ((read_length = fread(file_content, sizeof(char), offset, f))) {
                      file_content += read_length;
                      offset -= read_length;
                    }

                    if (compress_mode == U_COMPRESS_GZIP) {
                      if (deflateInit2(&defstream,
                                       Z_DEFAULT_COMPRESSION,
                                       Z_DEFLATED,
                                       U_GZIP_WINDOW_BITS | U_GZIP_ENCODING,
                                       8,
                                       Z_DEFAULT_STRATEGY) != Z_OK) {
                        y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflateInit (gzip)");
                        ret = U_CALLBACK_ERROR;
                      }
                    } else {
                      if (deflateInit(&defstream, Z_BEST_COMPRESSION) != Z_OK) {
                        y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflateInit (deflate)");
                        ret = U_CALLBACK_ERROR;
                    }
                    if (ret == U_CALLBACK_CONTINUE) {
                      do {
                        if ((data_zip = o_realloc(data_zip, data_zip_len+_U_W_BLOCK_SIZE)) != NULL) {
                          defstream.avail_out = _U_W_BLOCK_SIZE;
                          defstream.next_out = ((Bytef *)data_zip)+data_zip_len;
                          switch ((res = deflate(&defstream, Z_FINISH))) {
                            case Z_OK:
                            case Z_STREAM_END:
                            case Z_BUF_ERROR:
                              break;
                            default:
                              y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflate %d", res);
                              ret = U_CALLBACK_ERROR;
                              break;
                          }
                          data_zip_len += _U_W_BLOCK_SIZE - defstream.avail_out;
                        } else {
                          y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error allocating resources for data_zip");
                      } while (U_CALLBACK_CONTINUE == ret && defstream.avail_out == 0);

                        if (compress_mode == U_COMPRESS_GZIP) {
                          if (config->allow_cache_compressed) {
                            u_map_put_binary(&config->gzip_files, file_requested, data_zip, 0, defstream.total_out);
                          ulfius_set_binary_body_response(response, 200, u_map_get(&config->gzip_files, file_requested), u_map_get_length(&config->gzip_files, file_requested));
                        } else {
                          if (config->allow_cache_compressed) {
                            u_map_put_binary(&config->deflate_files, file_requested, data_zip, 0, defstream.total_out);
                          ulfius_set_binary_body_response(response, 200, u_map_get(&config->deflate_files, file_requested), u_map_get_length(&config->deflate_files, file_requested));
                        u_map_put(response->map_header, U_CONTENT_HEADER, compress_mode==U_COMPRESS_GZIP?U_ACCEPT_GZIP:U_ACCEPT_DEFLATE);
                    deflateEnd(&defstream);
                    o_free(data_zip);
                    y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error allocating resource for file_content or data_zip");
                    ret = U_CALLBACK_ERROR;
                  o_free(file_content_orig);
                fclose(f);
                if (((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404 == NULL) {
                  ret = U_CALLBACK_IGNORE;
                } else {
                  ulfius_add_header_to_response(response, "Location", ((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404);
                  response->status = 302;
                }
              pthread_mutex_unlock(&config->lock);
              y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error pthread_lock_mutex");
              ret = U_CALLBACK_ERROR;
 * Version 20220425
Fixed code:  * Version 20220428
#include <limits.h>
#include <stdlib.h>
  char * file_requested, * file_path, * url_dup_save, * real_path = NULL;
    real_path = realpath(file_path, NULL);
    if (0 == o_strncmp(((struct _u_compressed_inmemory_website_config *)user_data)->files_path, real_path, o_strlen(((struct _u_compressed_inmemory_website_config *)user_data)->files_path))) {
      f = fopen (file_path, "rb");
      if (f) {
        fseek (f, 0, SEEK_END);
        length = ftell (f);
        fseek (f, 0, SEEK_SET);

        content_type = u_map_get_case(&((struct _u_compressed_inmemory_website_config *)user_data)->mime_types, get_filename_ext(file_requested));
        if (content_type == NULL) {
          content_type = u_map_get(&((struct _u_compressed_inmemory_website_config *)user_data)->mime_types, "*");
          y_log_message(Y_LOG_LEVEL_WARNING, "Static File Server - Unknown mime type for extension %s", get_filename_ext(file_requested));
        }
        u_map_put(response->map_header, "Content-Type", content_type);
        u_map_copy_into(response->map_header, &((struct _u_compressed_inmemory_website_config *)user_data)->map_header);
        if (ulfius_set_stream_response(response, 200, callback_static_file_uncompressed_stream, callback_static_file_uncompressed_stream_free, length, CHUNK, f) != U_OK) {
          y_log_message(Y_LOG_LEVEL_ERROR, "Static File Server - Error ulfius_set_stream_response");
        }
        if (((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404 == NULL) {
          ret = U_CALLBACK_IGNORE;
        } else {
          ulfius_add_header_to_response(response, "Location", ((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404);
          response->status = 302;
        }
      o_free(url_dup_save);
    } else {
      response->status = 403;
    free(real_path); // realpath uses malloc

  char * file_requested, * file_path, * url_dup_save, * data_zip = NULL, * real_path = NULL;


            real_path = realpath(file_path, NULL);
            if (0 == o_strncmp(((struct _u_compressed_inmemory_website_config *)user_data)->files_path, real_path, o_strlen(((struct _u_compressed_inmemory_website_config *)user_data)->files_path))) {
              if (!pthread_mutex_lock(&config->lock)) {
                f = fopen (file_path, "rb");
                if (f) {
                  content_type = u_map_get_case(&config->mime_types, get_filename_ext(file_requested));
                  if (content_type == NULL) {
                    content_type = u_map_get(&config->mime_types, "*");
                    y_log_message(Y_LOG_LEVEL_WARNING, "Static File Server - Unknown mime type for extension %s", get_filename_ext(file_requested));
                  }
                  if (!string_array_has_value((const char **)config->mime_types_compressed, content_type)) {
                    compress_mode = U_COMPRESS_NONE;
                  }
                  u_map_put(response->map_header, "Content-Type", content_type);
                  u_map_copy_into(response->map_header, &config->map_header);

                  fseek (f, 0, SEEK_END);
                  offset = length = ftell (f);
                  fseek (f, 0, SEEK_SET);

                  if (length) {
                    if ((file_content_orig = file_content = o_malloc(length)) != NULL && (data_zip = o_malloc((2*length)+20)) != NULL) {
                      defstream.zalloc = u_zalloc;
                      defstream.zfree = u_zfree;
                      defstream.opaque = Z_NULL;
                      defstream.avail_in = (uInt)length;
                      defstream.next_in = (Bytef *)file_content;
                      while ((read_length = fread(file_content, sizeof(char), offset, f))) {
                        file_content += read_length;
                        offset -= read_length;

                      if (compress_mode == U_COMPRESS_GZIP) {
                        if (deflateInit2(&defstream,
                                         Z_DEFAULT_COMPRESSION,
                                         Z_DEFLATED,
                                         U_GZIP_WINDOW_BITS | U_GZIP_ENCODING,
                                         8,
                                         Z_DEFAULT_STRATEGY) != Z_OK) {
                          y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflateInit (gzip)");
                      } else {
                        if (deflateInit(&defstream, Z_BEST_COMPRESSION) != Z_OK) {
                          y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflateInit (deflate)");
                          ret = U_CALLBACK_ERROR;
                        }
                      }
                        do {
                          if ((data_zip = o_realloc(data_zip, data_zip_len+_U_W_BLOCK_SIZE)) != NULL) {
                            defstream.avail_out = _U_W_BLOCK_SIZE;
                            defstream.next_out = ((Bytef *)data_zip)+data_zip_len;
                            switch ((res = deflate(&defstream, Z_FINISH))) {
                              case Z_OK:
                              case Z_STREAM_END:
                              case Z_BUF_ERROR:
                                break;
                              default:
                                y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error deflate %d", res);
                                ret = U_CALLBACK_ERROR;
                                break;
                            }
                            data_zip_len += _U_W_BLOCK_SIZE - defstream.avail_out;
                          } else {
                            y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error allocating resources for data_zip");
                            ret = U_CALLBACK_ERROR;
                        } while (U_CALLBACK_CONTINUE == ret && defstream.avail_out == 0);

                        if (ret == U_CALLBACK_CONTINUE) {
                          if (compress_mode == U_COMPRESS_GZIP) {
                            if (config->allow_cache_compressed) {
                              u_map_put_binary(&config->gzip_files, file_requested, data_zip, 0, defstream.total_out);
                            }
                            ulfius_set_binary_body_response(response, 200, u_map_get(&config->gzip_files, file_requested), u_map_get_length(&config->gzip_files, file_requested));
                          } else {
                            if (config->allow_cache_compressed) {
                              u_map_put_binary(&config->deflate_files, file_requested, data_zip, 0, defstream.total_out);
                            }
                            ulfius_set_binary_body_response(response, 200, u_map_get(&config->deflate_files, file_requested), u_map_get_length(&config->deflate_files, file_requested));
                          u_map_put(response->map_header, U_CONTENT_HEADER, compress_mode==U_COMPRESS_GZIP?U_ACCEPT_GZIP:U_ACCEPT_DEFLATE);
                      deflateEnd(&defstream);
                      o_free(data_zip);
                    } else {
                      y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error allocating resource for file_content or data_zip");
                      ret = U_CALLBACK_ERROR;
                    o_free(file_content_orig);
                  }
                  fclose(f);
                } else {
                  if (((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404 == NULL) {
                    ret = U_CALLBACK_IGNORE;
                    ulfius_add_header_to_response(response, "Location", ((struct _u_compressed_inmemory_website_config *)user_data)->redirect_on_404);
                    response->status = 302;
                pthread_mutex_unlock(&config->lock);
                y_log_message(Y_LOG_LEVEL_ERROR, "callback_static_compressed_inmemory_website - Error pthread_lock_mutex");
                ret = U_CALLBACK_ERROR;
              response->status = 403;
            free(real_path); // realpath uses malloc
      } else {
        ret = callback_static_file_uncompressed(request, response, user_data);
    } else {
      ret = callback_static_file_uncompressed(request, response, user_data);
 * Version 20220428