Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Process] Permission Denied writing to sf_proc_00.out.lock when file is generated by a different AppPoolIdentity User #37294

Closed
@JasonStephensTAMU

Description

@JasonStephensTAMU

Symfony version(s) affected: 4.4.8|5.1.2

  • OS: Windows 10
  • Server: IIS version 10
  • PHP version 7.3
  • Laravel version 6.18

Note: Bug originally encountered on Laravel 6.18 using symfony/process 4.4.8. I have replicated the issue on a fresh symfony/skeleton 5.1.2 base with the process package required through composer.

Description

When two or more sites are running IIS / FastCGI as different AppPoolIdentity users with limited group permissions to the sys_temp_dir, the first to write sf_proc_00.* files used by the Process class takes ownership, however any additional sites that attempt to write to this file will receive the following error:

A temporary file could not be opened to write the process output: fopen(C:\WINDOWS\TEMP\sf_proc_00.out.lock): failed to open stream: Permission denied

Our permissions are configured to prevent one site from modifying temporary files that are created by another.

How to reproduce

  1. In IIS: Add two sites using the Symfony\Process component with different Application Pools
  2. In IIS Application Pools: For both application pools used, select "Advanced Settings" and confirm the "Identity" field is set to the built-in "ApplicationPoolIdentity" account,
  3. In IIS Feature Panel: For both sites, select "Authentication" > "Anonymous Authentication", ensure it is enabled and click "Edit..." then select "Application Pool Identity" and click OK.
  4. Set limited permissions for the IIS_IUSRS group on the sys_temp_dir (this is C:\Windows\Temp on my machine). Verify the Users group does not exceed these permissions.
    • Permissions used in testing: List folder / read data, Read attributes, Read extended attributes, Create files / write data, Read permissions
  5. For both sites, create some code that uses the Process class to run a command. (See Additional Context for the sample code I used to test)
  6. In Site A, run the code using the Process class and check the sys_temp_dir to confirm sf_proc_00 files were generated.
  7. In Site B, attempt to run the code using the Process class to throw the Permission Denied error.

Possible Solution

Perhaps a solution could be incrementing the filename when the lock files exist, but cannot be opened for writing, and removing the sf_proc_## files used in the WindowsPipes destructor.

I have limited experience in using the Process class, I'll post the code that has fixed it in my particular instance, however I do not know if it is ideal for all use cases.

symfony\process\Pipes\WindowsPipes.php, Line 58: (Try next iteration if lock file exists)

                    if (!$h = fopen($file.'.lock', 'w')) {
                        if (file_exists($file.'.lock'))
                            continue 2;

                        restore_error_handler();
                        throw new RuntimeException('A temporary file could not be opened to write the process output: '.$lastError);
                    }

symfony\process\Pipes\WindowsPipes.php, Line 88: (Remove files on destruct)

    public function __destruct()
    {
        $this->close();

        foreach ($this->files as $file) {
            unlink($file);
            unlink($file.'.lock');
        }
    }

Additional context

During replication, I made a simple controller that dumps output of the dir command for testing:

src\Controller\HomeController.php:

<?php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

class HomeController
{
    public function index()
    {
        $process = new Process(['dir']);
        $process->run();

        if(!$process->isSuccessful())
            throw new ProcessFailedException($process);

        return new Response($process->getOutput());
    }
}

And attached it to the base route:

config\routes.yaml:

index:
    path: /
    controller: App\Controller\HomeController::index

The same code was used on both Site A and B.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions