tags 649522 + patch
quit

Jonathan Nieder wrote:
> Eduard Bloch wrote:

>> Now I have also tried a random
>> package using liblzma* (fsarchiver) and it's FTBFSing too.
>>
>> dpkg-shlibdeps: warning: can't parse dependency liblzma_private_symbols
>> dpkg-shlibdeps: error: invalid dependency got generated: liblzma5 (>= 
>> 5.1.1alpha+20110809), libcomerr2 (>= 1.01), liblzma_private_symbols, 
>> libgpg-error0 (>= 1.10), libbz2-1.0, libgcrypt11 (>= 1.4.5), libc6 (>= 
>> 2.3.2), liblzo2-2, libuuid1 (>= 2.16), e2fslibs (>= 1.41.99), zlib1g (>= 
>> 1:1.1.4), libblkid1 (>= 2.16)
>
> Weird.  *tries it*

Workaround attached.
From: Jonathan Nieder <[email protected]>
Subject: liblzma: check if liblzma.so.2 is loaded to detect old callers

ld.so and ld.gold have bugs that could cause the lzma_code@Base
compatibility symbol to be underused or overused, respectively.  See

 http://sourceware.org/PR13521
 http://sourceware.org/PR12977

Any caller intending to use liblzma2 would include a DT_NEEDED entry
for it, so we can reliably detect the presence of such callers in a
process image with dlopen("liblzma.so.2", RTLD_NOLOAD).  Make that
check in lzma_strm_init and cache the result in strm->internal to
avoid slowing down the fastpath with filesystem access.

If any old callers are sharing the process image (think: during a
partial upgrade, running an app that uses a mixture of old and new
libraries that both use liblzma), stop examining reserved fields at
reserved_ptr4 to avoid spurious errors and segfaults.  In the usual
case when all callers are new, on the other hand, do examine all
reserved fields to match the ABI used elsewhere and help developers
avoid surprises.

This behavior is the same as we already had, except the trigger for
turning on backward compatible mode is "liblzma.so.2 is loaded"
instead of "the loader chose to use the lzma_code@Base symbol".
---
 src/liblzma/Makefile.am     |    2 +-
 src/liblzma/common/common.c |  132 ++++++++++++++++++++-----------------------
 src/liblzma/common/common.h |    4 ++
 3 files changed, 66 insertions(+), 72 deletions(-)

diff --git a/src/liblzma/Makefile.am b/src/liblzma/Makefile.am
index ac2d1ed1..41cb02f6 100644
--- a/src/liblzma/Makefile.am
+++ b/src/liblzma/Makefile.am
@@ -24,7 +24,7 @@ liblzma_la_CPPFLAGS = \
        -I$(top_srcdir)/src/liblzma/simple \
        -I$(top_srcdir)/src/common \
        -DTUKLIB_SYMBOL_PREFIX=lzma_
-liblzma_la_LDFLAGS = -no-undefined -version-info 5:0:0
+liblzma_la_LDFLAGS = -no-undefined -version-info 5:0:0 -ldl
 
 if COND_SYMVERS
 EXTRA_DIST += liblzma.map
diff --git a/src/liblzma/common/common.c b/src/liblzma/common/common.c
index 924b0a14..d8c28a42 100644
--- a/src/liblzma/common/common.c
+++ b/src/liblzma/common/common.c
@@ -12,6 +12,8 @@
 
 #include "common.h"
 
+#include <dlfcn.h>
+
 
 /////////////
 // Version //
@@ -141,6 +143,17 @@ lzma_next_end(lzma_next_coder *next, lzma_allocator 
*allocator)
 // External to internal API wrapper //
 //////////////////////////////////////
 
+static bool
+compat_mode(void)
+{
+       void *handle = dlopen("liblzma.so.2", RTLD_LAZY | RTLD_NOLOAD);
+       if (handle) {
+               dlclose(handle);
+               return true;
+       }
+       return false;
+}
+
 extern lzma_ret
 lzma_strm_init(lzma_stream *strm)
 {
@@ -154,6 +167,7 @@ lzma_strm_init(lzma_stream *strm)
                        return LZMA_MEM_ERROR;
 
                strm->internal->next = LZMA_NEXT_CODER_INIT;
+               strm->internal->compat_mode = compat_mode();
        }
 
        memzero(strm->internal->supported_actions,
@@ -168,9 +182,54 @@ lzma_strm_init(lzma_stream *strm)
 }
 
 
-static lzma_ret
-lzma_code_unchecked(lzma_stream *strm, lzma_action action)
+// Before v5.0.0~6 (liblzma: A few ABI tweaks to reserve space in
+// structures, 2010-10-23), the reserved fields in lzma_stream were:
+//
+//     void *reserved_ptr1;
+//     void *reserved_ptr2;
+//     uint64_t reserved_int1;
+//     uint64_t reserved_int2;
+//     lzma_reserved_enum reserved_enum1;
+//     lzma_reserved_enum reserved_enum2;
+//
+// Nowadays there are two more pointers between reserved_ptr2 and
+// reserved_int1 and two size_t’s between reserved_int2 and
+// reserved_enum1.
+//
+// When strm->internal->compat_mode is set, limit the checks of
+// reserved fields to fields that were present in the old ABI, to avoid
+// segfaults and spurious "Unsupported options" from callers sharing
+// the process image that expect the old ABI.
+extern LZMA_API(lzma_ret)
+lzma_code(lzma_stream *strm, lzma_action action)
 {
+       // Sanity checks
+       if ((strm->next_in == NULL && strm->avail_in != 0)
+                       || (strm->next_out == NULL && strm->avail_out != 0)
+                       || strm->internal == NULL
+                       || strm->internal->next.code == NULL
+                       || (unsigned int)(action) > LZMA_FINISH
+                       || !strm->internal->supported_actions[action])
+               return LZMA_PROG_ERROR;
+
+       // Check if unsupported members have been set to non-zero or non-NULL,
+       // which would indicate that some new feature is wanted.
+       if (strm->reserved_ptr1 != NULL
+                       || strm->reserved_ptr2 != NULL
+                       || strm->reserved_ptr3 != NULL
+                       || strm->reserved_ptr4 != NULL)
+               return LZMA_OPTIONS_ERROR;
+
+       if (strm->internal->compat_mode)
+               ; /* Enough checks. */
+       else if (strm->reserved_int1 != 0
+                       || strm->reserved_int2 != 0
+                       || strm->reserved_int3 != 0
+                       || strm->reserved_int4 != 0
+                       || strm->reserved_enum1 != LZMA_RESERVED_ENUM
+                       || strm->reserved_enum2 != LZMA_RESERVED_ENUM)
+               return LZMA_OPTIONS_ERROR;
+
        switch (strm->internal->sequence) {
        case ISEQ_RUN:
                switch (action) {
@@ -284,75 +343,6 @@ lzma_code_unchecked(lzma_stream *strm, lzma_action action)
        return ret;
 }
 
-extern LZMA_API(lzma_ret)
-lzma_code_standard(lzma_stream *strm, lzma_action action)
-{
-       // Sanity checks
-       if ((strm->next_in == NULL && strm->avail_in != 0)
-                       || (strm->next_out == NULL && strm->avail_out != 0)
-                       || strm->internal == NULL
-                       || strm->internal->next.code == NULL
-                       || (unsigned int)(action) > LZMA_FINISH
-                       || !strm->internal->supported_actions[action])
-               return LZMA_PROG_ERROR;
-
-       // Check if unsupported members have been set to non-zero or non-NULL,
-       // which would indicate that some new feature is wanted.
-       if (strm->reserved_ptr1 != NULL
-                       || strm->reserved_ptr2 != NULL
-                       || strm->reserved_ptr3 != NULL
-                       || strm->reserved_ptr4 != NULL
-                       || strm->reserved_int1 != 0
-                       || strm->reserved_int2 != 0
-                       || strm->reserved_int3 != 0
-                       || strm->reserved_int4 != 0
-                       || strm->reserved_enum1 != LZMA_RESERVED_ENUM
-                       || strm->reserved_enum2 != LZMA_RESERVED_ENUM)
-               return LZMA_OPTIONS_ERROR;
-
-       return lzma_code_unchecked(strm, action);
-}
-__asm__(".symver lzma_code_standard,lzma_code@@XZ_5.0");
-
-// Before v5.0.0~6 (liblzma: A few ABI tweaks to reserve space in
-// structures, 2010-10-23), the reserved fields in lzma_stream were:
-//
-//     void *reserved_ptr1;
-//     void *reserved_ptr2;
-//     uint64_t reserved_int1;
-//     uint64_t reserved_int2;
-//     lzma_reserved_enum reserved_enum1;
-//     lzma_reserved_enum reserved_enum2;
-//
-// Nowadays there are two more pointers between reserved_ptr2 and
-// reserved_int1 and two size_t’s between reserved_int2 and
-// reserved_enum1.
-//
-// This version of the lzma_code symbol is used by apps built before
-// symbol versioning was introduced.  It limits the checks of reserved
-// fields to fields that were present in the old ABI, to avoid segfaults
-// and spurious "Unsupported options" errors.
-extern LZMA_API(lzma_ret)
-lzma_code_compat(lzma_stream *strm, lzma_action action)
-{
-       if ((strm->next_in == NULL && strm->avail_in != 0)
-                       || (strm->next_out == NULL && strm->avail_out != 0)
-                       || strm->internal == NULL
-                       || strm->internal->next.code == NULL
-                       || (unsigned int)(action) > LZMA_FINISH
-                       || !strm->internal->supported_actions[action])
-               return LZMA_PROG_ERROR;
-
-       if (strm->reserved_ptr1 != NULL
-                       || strm->reserved_ptr2 != NULL
-                       || strm->reserved_ptr3 != NULL
-                       || strm->reserved_ptr4 != NULL)
-               return LZMA_OPTIONS_ERROR;
-
-       return lzma_code_unchecked(strm, action);
-}
-__asm__(".symver lzma_code_compat,lzma_code@");
-
 
 extern LZMA_API(void)
 lzma_end(lzma_stream *strm)
diff --git a/src/liblzma/common/common.h b/src/liblzma/common/common.h
index 45aba4f0..ce19e026 100644
--- a/src/liblzma/common/common.h
+++ b/src/liblzma/common/common.h
@@ -197,6 +197,10 @@ struct lzma_internal_s {
        /// Indicates which lzma_action values are allowed by next.code.
        bool supported_actions[4];
 
+       /// Indicates whether we are sharing a process image with
+       /// liblzma.so.2 and need to tread carefully.
+       bool compat_mode;
+
        /// If true, lzma_code will return LZMA_BUF_ERROR if no progress was
        /// made (no input consumed and no output produced by next.code).
        bool allow_buf_error;
-- 
1.7.10.2

Reply via email to