none
Security Permissions in Threads upon ExecutionContext.SuppressFlow() RRS feed

  • Question

  • In the code listed below, I have denied read permission to the entire assembly. After which I call SupressFlow. Then, I call the "readboot" method in a thread. Here the code execution passes BOTH declaritive and imperitive security demands, but fails at the ReadAllText method with security exception, "Demand for Read failed". Why is that? That seems odd. Does anyone have a clue?

     

    Code Snippet

    using System;

    using System.Threading;

    using System.IO;

    using System.Security.Permissions;

    [assembly: FileIOPermission(SecurityAction.RequestRefuse, Read = @"C:\boot.ini")]

    class Program

    {

    static void Main()

    {

    ExecutionContext.SuppressFlow();

    Thread th = new Thread(readboot);

    th.Start();

    th.Join();

    Console.ReadLine();

    }

    [FileIOPermission(SecurityAction.Demand, Read = @"C:\boot.ini")]

    static void readboot()

    {

    try

    {

    FileIOPermission fiop = new FileIOPermission(

       FileIOPermissionAccess.Read, @"C:\boot.ini");

    fiop.Demand();

    }

    catch (Exception ex)

    {

    Console.WriteLine(ex.Message);

    return;

    }

    //Exception occurs here (FileIOPermission demand fails)

    string FileText = File.ReadAllText(@"C:\boot.ini");

    }

    }

     

     

    Monday, March 24, 2008 8:44 PM

Answers

  • The CAS check for permissions of the current method by walking back the stack frames. And normally, when you start a new thread, the CLR will copy the stack frames information of the parent thread.

    --which is after it passes through both declarative and imperative security checks!
    This is because the ExecutionContext.SuppressFlow() wipes the stack frames information of the parent thread. So the readboot method is the only one in the stack frames thus it has no caller now. The security check passed since the CAS has no stack frames to walk through and check.

    But when you call the file IO method, there's at least one method(the readboot) pushed under the stack frames, and the CAS finds that the owner assembly of the method has insufficient permission to read the file, so it throws the exception.

    If you write your code like this:

    ExecutionContext.SuppressFlow();
    Thread th = new Thread(ThreadWork);

    static void ThreadWork()
    {
        readboot();
    }

    The declarative and imperative security checks will work since there is a method (caller of the readboot method -- ThreadWork ) in the stack frames to check.



    • Edited by Feng Chen Wednesday, June 18, 2008 12:57 PM Type and grammar errors corrected.
    Monday, March 31, 2008 5:02 AM

All replies

  • Could you please cover the exception in more details? You can post its stack trace, and inner exception here.

    The exception may not because of the CAS policy, but the access right of the user to the file.
    Wednesday, March 26, 2008 8:05 AM
  • Thanks Feng for looking into this. I don't think user rights is the issue - this happens only with the convergence of these three conditions:

    1. Assembly-level FileIOPermission attribute - RequestRefuse
    2. ExecutionContext.SupressFlow();
    3. Start the readboot method in a Thread.

    If you omit any one of these, the code executes as expected - i.e. it will either run through successfully, or the declaritive attribute decorating readboot() will stop it.

     

    Also note - this happens with both VS2005 and 2008. You can copy the posted code in a console app and test out it for yourself.

     

    InnerException:  null

     

    Stack Trace:

       at System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
       at System.Security.CodeAccessPermission.Demand()
       at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy)
       at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
       at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize)
       at System.IO.StreamReader..ctor(String path, Encoding encoding)
       at System.IO.File.ReadAllText(String path, Encoding encoding)
       at System.IO.File.ReadAllText(String path)
       at Program.readboot() in C:\Documents and Settings\jagdalev\Local Settings\Application Data\Temporary Projects\ConsoleApplication1\Program.cs:line 56
       at System.Threading.ThreadHelper.ThreadStart()

     

     

    Wednesday, March 26, 2008 6:24 PM
  • Based on my tesing, only removing the RequestRefuse attribute will let the exception go away.

    Actually,
    requestRefuse basically tells the CLR security system: "this application should never be granted the named permission". So, for example, if the following line is there:

    [assembly: FileIOPermission(SecurityAction.RequestRefuse, Read = @"C:\boot.ini")]

    that would mean that assembly with it will not be granted reading the particular file, even if run from the environment that otherwise would let it.

    Thursday, March 27, 2008 6:58 AM
  • Actually the issue is *not* RequestRefuse. The issue is why does the declarative and imperative attributes demands for the "readboot()" method fail?

     

    If you want to test it, you will see that the error occurs on the line :

    string FileText = File.ReadAllText(@"C:\boot.ini");

    which is after it passes through both declarative and imperative security checks!

     

    Now if you remove the line ExecutionContext.SuppressFlow();

    you will see that the declarative attribute demand that produces the exception (which is what I would expect).

     

    Similarly if you remove the thread statements and directly call readboot(), you will see that the declarative attribute again traps the exception (again, as expected).

     

    Do you see the problem?

    Thursday, March 27, 2008 11:57 PM
  • The CAS check for permissions of the current method by walking back the stack frames. And normally, when you start a new thread, the CLR will copy the stack frames information of the parent thread.

    --which is after it passes through both declarative and imperative security checks!
    This is because the ExecutionContext.SuppressFlow() wipes the stack frames information of the parent thread. So the readboot method is the only one in the stack frames thus it has no caller now. The security check passed since the CAS has no stack frames to walk through and check.

    But when you call the file IO method, there's at least one method(the readboot) pushed under the stack frames, and the CAS finds that the owner assembly of the method has insufficient permission to read the file, so it throws the exception.

    If you write your code like this:

    ExecutionContext.SuppressFlow();
    Thread th = new Thread(ThreadWork);

    static void ThreadWork()
    {
        readboot();
    }

    The declarative and imperative security checks will work since there is a method (caller of the readboot method -- ThreadWork ) in the stack frames to check.



    • Edited by Feng Chen Wednesday, June 18, 2008 12:57 PM Type and grammar errors corrected.
    Monday, March 31, 2008 5:02 AM
  • Your explaination makes sense. Thanks a bunch.

     

    I guess what it means that we cannot always rely on declarative and imperative demands - there are some pecular conditions when the security checks do not apply.

    Tuesday, April 1, 2008 3:51 PM