prevent exceeding 32 bit memory limitations with multithreaded xz compression
[platform/upstream/rpm.git] / rpmio / rpmio.c
index 7add853..e8c55ff 100644 (file)
@@ -6,6 +6,10 @@
 #include <stdarg.h>
 #include <errno.h>
 #include <ctype.h>
+#if defined(__linux__)
+#include <sys/personality.h>
+#endif
+#include <sys/utsname.h>
 
 #include <rpm/rpmlog.h>
 #include <rpm/rpmmacro.h>
@@ -952,6 +956,45 @@ static LZFILE *lzopen_internal(const char *path, const char *mode, int fd, int x
                    .filters = NULL,
                    .check = LZMA_CHECK_SHA256 };
 
+#if __WORDSIZE == 32
+               /* In 32 bit environment, required memory easily exceeds memory address
+                * space limit if compressing using multiple threads.
+                * By setting a memory limit, liblzma will automatically adjust number
+                * of threads to avoid exceeding memory.
+                */
+               if (threads > 1) {
+                   struct utsname u;
+                   uint32_t memlimit = (SIZE_MAX>>1) + (SIZE_MAX>>3);
+                   uint64_t memory_usage;
+                   /* While a 32 bit linux kernel will have an address limit of 3GiB
+                    * for processes (which is why set the memory limit to 2.5GiB as a safety
+                    * margin), 64 bit kernels will have a limit of 4GiB for 32 bit binaries.
+                    * Therefore the memory limit should be higher if running on a 64 bit
+                    * kernel, so we increase it to 3,5GiB.
+                    */
+                   uname(&u);
+                   if (strstr(u.machine, "64") || strstr(u.machine, "s390x")
+#if defined(__linux__)
+                                   || ((personality(0xffffffff) & PER_MASK) == PER_LINUX32)
+#endif
+                           )
+                       memlimit += (SIZE_MAX>>2);
+
+                   /* keep reducing the number of threads untill memory usage gets below limit */
+                   while ((memory_usage = lzma_stream_encoder_mt_memusage(&mt_options)) > memlimit) {
+                       /* number of threads shouldn't be able to hit zero with compression
+                        * settings aailable to set through rpm... */
+                       assert(--mt_options.threads != 0);
+                   }
+                   lzma_memlimit_set(&lzfile->strm, memlimit);
+
+                   if (threads != (int)mt_options.threads)
+                       rpmlog(RPMLOG_NOTICE,
+                               "XZ: Adjusted the number of threads from %d to %d to not exceed the memory usage limit of %lu bytes",
+                               threads, mt_options.threads, memlimit);
+               }
+#endif
+
                ret = lzma_stream_encoder_mt(&lzfile->strm, &mt_options);
            }
        } else {