Skip to content

Application can crash if linked against both libppd and libcups #52

Open

Description

@alexpevzner

CUPS, on its sources, implements (duplicates implementation) of some, but not all, libppd functions, and exports them from the libcups.so library.

For example, the ppdOpenFd and ppdClose functions are exported from the libcups.so, while ppdLoadAttributes is not.

If the application is linked against both libcups.so and libppd.so libraries, the following scenario is possible:

  • application calls ppdOpenFd from the libcups.so
  • application calls ppdLoadAttributes, and this call is now goes into the libppd.so library. This function modifies the ppd_file_t structure, but the definition of this type is not exactly the same between these two libraries (the actual difference seems to be in the ppd_cache_t in the libppd vs _ppd_cache_s in the libcups.so)
  • application calls ppdClose and this call goes to the libcups.so and crashes there in attempt to destroy the _ppd_cache_s structure, which was actually created in the libppd.so

The exact behaviour depends on a link order; if libppd is linked first, everything is OK, while if libcups is linked first, application crashed.

I've caught this issue at Fedora 40 with cups-2.4.11-1.fc40.x86_64 and libppd-2.1~b1-2.fc40.x86_64 packages installed, but most likely it may affect other distros and other version.

It is hard to say what could be a perfect solution for this problem, because libcups header files announces exporting many ppdXXX functions, so this is a part of the libcups public API. And without linking of the libcups.so the libppd.so functions that use ipp_t and ipp_attribute_t become unusable, because these types are opaque and accessible only via libcups.so-exported API...

Activity

  1. michaelrsweet commented on Nov 9, 2024

    @michaelrsweet
    Member

    You have that backwards - libppd implements libcups functions.

  2. alexpevzner commented on Nov 10, 2024

    @alexpevzner
    MemberAuthor

    What if we mark all duplicated symbols as weak at the libcups.so and somewhere document this behaviour change?

    If it breaks something, it will (at the first glance) break only apps that really and intentionally use mix of the overlapping APIs from the both libraries, which is incorrect and extremely dangerous by itself.

    It is not a perfect solution, but it least it will make things deterministic and result will not depend on a link order.

  3. michaelrsweet commented on Nov 10, 2024

    @michaelrsweet
    Member

    Again, libcups.so.2 (CUPS 2.x libcups) always has the PPD functions in it. Any notion of doing weak linkage (not supported on all CUPS platforms) is incorrect since libcups is the original library.

    Either libppd should rename its functions or it should not include them when building on a system with CUPS 2.x since libcups will provide them.

  4. alexpevzner commented on Nov 11, 2024

    @alexpevzner
    MemberAuthor

    I don't see any equivalent of the ppdLoadAttributes function in the libcups.so...

  5. michaelrsweet commented on Nov 11, 2024

    @michaelrsweet
    Member

    @alexpevzner It is called _ppdCacheCreateWithFile in libcups.so.2...

  6. tillkamppeter commented on Nov 11, 2024

    @tillkamppeter
    Member

    @alexpevzner Note that libppd is putting together the PPD-related API functions of libcups, the functions needed for the PPD compiler (*.drv -> *.ppd) utilities, functions of cups-driverd (for PPD collection handling), and making some internal libcups functions public API. There are also added several new functions. This is all clearly marked in ppd/ppd.h, in the part of the function prototypes at the end. There I have also told for what the new functions are good for and from where in CUPS 2.x they come from.

  7. michaelrsweet commented on Nov 11, 2024

    @michaelrsweet
    Member

    @tillkamppeter One thing you could do here is change the main header to have a block of defines that rename the functions to names that don't conflict with libcups, e.g.:

    #define ppdClose libppdClose
    #define ppdCollect libppdCollect
    #define ppdCollect2 libppdCollect2
    #define ppdConflicts libppdConflicts
    #define ppdEmit libppdEmit
    #define ppdEmitAfterOrder libppdEmitAfterOrder
    #define ppdEmitFd libppdEmitFd
    #define ppdEmitJCL libppdEmitJCL
    #define ppdEmitJCLEnd libppdEmitJCLEnd
    #define ppdEmitString libppdEmitString
    #define ppdErrorString libppdErrorString
    #define ppdFindAttr libppdFindAttr
    #define ppdFindChoice libppdFindChoice
    #define ppdFindCustomOption libppdFindCustomOption
    #define ppdFindCustomParam libppdFindCustomParam
    #define ppdFindMarkedChoice libppdFindMarkedChoice
    #define ppdFindNextAttr libppdFindNextAttr
    #define ppdFindOption libppdFindOption
    #define ppdFirstCustomParam libppdFirstCustomParam
    #define ppdFirstOption libppdFirstOption
    #define ppdInstallableConflict libppdInstallableConflict
    #define ppdIsMarked libppdIsMarked
    #define ppdLastError libppdLastError
    #define ppdLocalize libppdLocalize
    #define ppdLocalizeAttr libppdLocalizeAttr
    #define ppdLocalizeIPPReason libppdLocalizeIPPReason
    #define ppdLocalizeMarkerName libppdLocalizeMarkerName
    #define ppdMarkDefaults libppdMarkDefaults
    #define ppdMarkOption libppdMarkOption
    #define ppdNextCustomParam libppdNextCustomParam
    #define ppdNextOption libppdNextOption
    #define ppdOpen libppdOpen
    #define ppdOpen2 libppdOpen2
    #define ppdOpenFd libppdOpenFd
    #define ppdOpenFile libppdOpenFile
    #define ppdPageLength libppdPageLength
    #define ppdPageSize libppdPageSize
    #define ppdPageSizeLimits libppdPageSizeLimits
    #define ppdPageWidth libppdPageWidth
    #define ppdSetConformance libppdSetConformance
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions