rschupp / PAR-Packer

(perl) Generate stand-alone executables, perl scripts and PAR files https://metacpan.org/pod/PAR::Packer
Other
48 stars 13 forks source link

Subroutine requires_firewall redefined #72

Open fantinuoli opened 1 year ago

fantinuoli commented 1 year ago

On Windows, after x calls of the app.exe created with pp, the app does not start and the error message I can get is the following:

Subroutine requires_firewall redefined at C:\Users\User\AppData\Local\Temp\par-416e61\cache-d08980143978b78da45c870adb39c4834fc93e55\f7e1bfba.pm line 104.

I cannot understand how this can happen randomly after x calls. Deleting par-416e61 and rerunning the exe solves the issue. What can be the cause?

rschupp commented 1 year ago

I cannot understand how this can happen randomly after x calls

Me neither 😕 Just for the record: can you state the OS you're using and the version of perl, PAR::Packer, PAR and Module::ScanDeps?

"Subroutine ... redefined" is typically just a warning and won't abort your program (unless someone does use warnings FATAL => ...), so that message may be just a red herring. Have you looked at the file mentioned in the message, can you identify where it came from?

fantinuoli commented 1 year ago

These are the info:

This is perl 5, version 32, subversion 1 (v5.32.1) built for MSWin32-x64-multi-thread

PAR::Packer: 1.052 PAR: 1.017 Module::ScanDeps: 1.31

Inspecting the entire par folder, I find only one item with "requires_firewall": Net::Config.pm

Note that the issue above (the logfile produced by the app looks like and endless print of the same statement (some loop?), can be found on only one machine...

rschupp commented 1 year ago

This is perl 5, version 32, subversion 1 (v5.32.1) built for MSWin32-x64-multi-thread

Strawberry Perl, I guess?

PAR::Packer: 1.052 PAR: 1.017

A little old, but I don't see how stuff fixed in newer versions might affect your problem.

Inspecting the entire par folder, I find only one item with "requires_firewall": Net::Config.pm

That's what I suspected as well. What's strange is the file from where the message apparently originates: files named 8-hex-numbers.pm in the top level of the par cache folder should be either a copy of the script packed by pp or copies of a well-known set of modules, but Net::Config isn't among them.

Anyway, my first suspicion when I see "a packed program stops working after several successful invocations" is some program periodically cleaning up the user's temp folder and randomly deleting files in the par cache folder. The files in the cache folder are written only when the folder doesn't exist. Once an invocation of the packed program sees the cache folder, it assumes it's complete. That's why blowing away the cache folder solves the problem (at least temporarily).

PAR::Packer uses a simple heuristic to work against such cleaners: all files extracted are timestamped with the time of extraction and one file (_canary_.txt or such) is created with a timestamp backdated by a few days. And the check for "can we skip extracting files" is actually: does the cache folder exist and contains _canary_.txt? If this heuristic fails and a cleaner randomly deletes files from the cache, I would typically expect the packed program to fail with "can't find module Foo" errors, but other ways are conceivable.

If you can get hold of the cache folder where the problem manifests itself you could compare its contents to the contents of a freshly extracted cache folder.

fantinuoli commented 1 year ago

Yes, I suspect too that some files may get deleted from the par cache folder. In the past I had a recurring case of a specific .txt file get routinely deleted from (some) macOS computers. I ended up avoiding the use of the module relying on that file.

I am wondering if adding a "file count" - at run time - of the files present in the cache folder may be a viable solution to avoid this behavior (which does not happen frequently, but it happens). The file number may be added in the canary.txt. Any variation would trigger a new unpacking of the file.

rschupp commented 1 year ago

Maybe PAR::Packer should just use another location (see #23) for the cache folder that isn't prone to cleaner shenanigans?

rschupp commented 1 year ago

It doesn't solve your problem, but I can reproduce it (on Linux with latest perl, PAR and PAR::Packer):

  1. pack the one-liner
    pp -o bad.exe -E "say qq[PAR_TEMP = $ENV{PAR_TEMP}]; require Net::Config; say $INC{'Net/Config.pm'};"
  2. run bad.exe once
  3. remove the file named in the second line of its output
  4. run bad.exe again

On the second run, the previously extracted Net/Config.pm has vanished, hence PAR::Packer falls back to the legacy "extract on demand" method and extracts this file from the exe (which is also a zip file) into one of these 8-hex-numbers.pm in the top level of the par cache folder. It executes that file and goes into an infinite loop here (~line 77)

my $file = __FILE__;
my $ref;
$file =~ s/Config.pm/libnet.cfg/;
if (-f $file) {
  $ref = eval { local $SIG{__DIE__}; do $file };

because __FILE__ (and hence $file) is something like /tmp/par-726f646572696368/cache-888dae6771dccfee8408e8b456df4bcde57f820e/71d26277.pm, i.e. not .../Config.pm, hence $file is unchanged after s/// and is loaded a second time via do $file.

I call that a bug (for not checking the success of the substitution and in general messing with __FILE__).