NixOS / nixpkgs

Nix Packages collection & NixOS
MIT License
18.4k stars 14.35k forks source link

wrapProgram --set PERL5LIB is incompatible with perl -T #263396

Open ToxicFrog opened 1 year ago

ToxicFrog commented 1 year ago

Describe the bug

It is conventional to package perl programs using wrapProgram --set PERL5LIB, so that they can find their libraries. One example of this is munin.

However, if the perl program uses perl -T (i.e. run in "taint checking" mode), this environment variable is considered "tainted" and is ignored, causing the program to fail at runtime. You can observe this in munin when running in cgi-graph mode, since the graph generator script uses perl -T and will die immediately with Can't locate Date/Manip.pm in @INC if run.

Steps To Reproduce

Run any wrapped program in nixpkgs that wraps PERL5LIBS and has a perl -T shebang.

Expected behavior

wrapProgram'd perl programs work even if they use perl -T.

Additional context

I'm honestly not sure what the right thing to do here is. I have a workaround for now with an overlay that strips the -T from all of the munin scripts, but I don't think that's a good blanket solution. There is a perl library that excludes $PERL5LIB from taint checking when loaded, but I don't know how to force the wrapped program to load it. I am mostly posting this in the hopes that other people who know more about perl than I do will come up with a solution.

Notify maintainers

@bjornfor -- as the munin maintainer, since this breaks common munin use-cases, but I think the actual fix might need to be in nixpkg's perl support.

Metadata

bjornfor commented 1 year ago

perl.withPackages is also implemented with the PERL5LIB environment variable.

The only fix I can think of is to get upstream perl to have another way to configure deps. Then we could use that in perl.withPackages at least.

For comparison, python.withPackages has this wrapper:

makeCWrapper '/nix/store/c3cjxhn73xa5s8fm79w95d0879bijp04-python3-3.10.13/bin/python3' \
    --set 'NIX_PYTHONPREFIX' '/nix/store/x6hnbzyflpsm1hawwkw9ji4l88n4fx6m-python3-3.10.13-env' \
    --set 'NIX_PYTHONEXECUTABLE' '/nix/store/x6hnbzyflpsm1hawwkw9ji4l88n4fx6m-python3-3.10.13-env/bin/python3.10' \
    --set 'NIX_PYTHONPATH' '/nix/store/x6hnbzyflpsm1hawwkw9ji4l88n4fx6m-python3-3.10.13-env/lib/python3.10/site-packages' \
    --set 'PYTHONNOUSERSITE' 'true'

So it seems to use nixpkgs specific setup.

florianjacob commented 11 months ago

For me, it is manifesting as munin-cron error Can't locate Date/Parse.pm in @INC (you may need to install the Date::Parse module), but I think it is the same cause.

ToxicFrog commented 11 months ago

Yep, that's the same issue -- Nix wiggles some environment variables so that Perl knows where to find its libraries, and -T tells it to (among other things) ignore those environment variables and use the compiled-in @INC, so this manifests as perl scripts being unable to find perl libraries that are ostensibly in their dependency closure.

ToxicFrog commented 11 months ago

Ok, I said that, but after updating, munin-cron is failing in the same way for me even with my patch, and the failing scripts aren't using -T.

It looks like munin needs Date::Parse now and that isn't packaged for nixos, so the munin package is just flat broken at head.

Here's my overlay:

final: prev: {
  munin = prev.munin.overrideAttrs (old: {
    # HACK HACK HACK
    # perl -T breaks makeWrapper --set PERL5LIB; see https://github.com/NixOS/nixpkgs/issues/263396
    # Copied from pkgs/servers/monitoring/munin/default.nix

    buildInputs = old.buildInputs ++ [ final.perlPackages.TimeDate ];
    postFixup = ''
      # Added this quick hack
      echo "Replacing perl -T with perl..."
      ${final.gnused}/bin/sed -E -i "s/perl -T/perl/" "$out"/www/cgi/*

      echo "Removing references to /usr/{bin,sbin}/ from munin plugins..."
      find "$out/lib/plugins" -type f -print0 | xargs -0 -L1 \
          ${final.gnused}/bin/sed -i -e "s|/usr/bin/||g" -e "s|/usr/sbin/||g" -e "s|\<bc\>|${final.bc}/bin/bc|g"

      if test -e $out/nix-support/propagated-build-inputs; then
          ln -s $out/nix-support/propagated-build-inputs $out/nix-support/propagated-user-env-packages
      fi

      # Added CGI, CGI::Fast, and TimeDate to the library list
      for file in "$out"/bin/munindoc "$out"/sbin/munin-* "$out"/lib/munin-* "$out"/www/cgi/*; do
          # don't wrap .jar files
          case "$file" in
              *.jar) continue;;
          esac
          wrapProgram "$file" \
            --set PERL5LIB "$out/${final.perlPackages.perl.libPrefix}:${with final.perlPackages; makePerlPath [
                  LogLog4perl IOSocketINET6 Socket6 URI DBFile DateManip TimeDate
                  HTMLTemplate FileCopyRecursive FCGI NetCIDR NetSNMP NetServer
                  ListMoreUtils DBDPg LWP final.rrdtool CGI CGIFast
                  ]}"
      done
    '';
  });
}

I should probably turn it into a proper PR, but I've got two others still waiting for merge and if I open too many I tend to lose track of them.

bjornfor commented 11 months ago

Here's one way to keep track: https://github.com/NixOS/nixpkgs/pulls/toxicfrog

liketechnik commented 1 month ago

The only fix I can think of is to get upstream perl to have another way to configure deps.

It kind of has with BEGIN { use lib '<PATH>'; };; although this requires modifying the original file, and cannot really applied in a wrapper, I think. For munin, I did the following to make cgi-graph work:

munin = super.munin.overrideAttrs (oldAttrs: let
      incText = ''
        use strict;
        BEGIN {
          use lib '$out/${super.perlPackages.perl.libPrefix}';
          use lib '${with super.perlPackages; makePerlPath [LogLog4perl]}';
          use lib '${with super.perlPackages; makePerlPath [IOSocketINET6]}';
          use lib '${with super.perlPackages; makePerlPath [Socket6]}';
          use lib '${with super.perlPackages; makePerlPath [URI]}';
          use lib '${with super.perlPackages; makePerlPath [DBFile]}';
          use lib '${with super.perlPackages; makePerlPath [TimeDate]}';
          use lib '${with super.perlPackages; makePerlPath [HTMLTemplate]}';
          use lib '${with super.perlPackages; makePerlPath [FileCopyRecursive]}';
          use lib '${with super.perlPackages; makePerlPath [FCGI]}';
          use lib '${with super.perlPackages; makePerlPath [CGI]}';
          use lib '${with super.perlPackages; makePerlPath [CGIFast]}';
          use lib '${with super.perlPackages; makePerlPath [NetCIDR]}';
          use lib '${with super.perlPackages; makePerlPath [NetSNMP]}';
          use lib '${with super.perlPackages; makePerlPath [NetServer]}';
          use lib '${with super.perlPackages; makePerlPath [ListMoreUtils]}';
          use lib '${with super.perlPackages; makePerlPath [DBDPg]}';
          use lib '${with super.perlPackages; makePerlPath [LWP]}';
          use lib '${with super.perlPackages; makePerlPath [super.rrdtool]}';
        };
      '';
    in {
      postFixup = oldAttrs.postFixup + ''
        sed -i -e "s|use strict;|${lib.strings.stringAsChars (x: if x == "\n" then " " else x) incText}|g" $out/www/cgi/.munin-cgi-graph-wrapped
      '';
    });