mmap_is_attached_or_noreserve

Ken Brown kbrown@cornell.edu
Thu Dec 26 19:35:21 GMT 2024


On 12/25/2024 6:06 PM, Ken Brown wrote:
> On 12/25/2024 1:08 PM, Ken Brown wrote:
>> Hi Corinna,
>>
>> I know you're on vacation, but I'm writing this now while it's fresh 
>> in my mind.  We can discuss it when you return.
>>
>> This is a follow-up to
>>
>>    https://cygwin.com/pipermail/cygwin-developers/2024- 
>> December/012715.html
>>
>> but I'm starting a new thread for clarity, and I'll start from scratch.
>>
>> I'm back to thinking that mmap_is_attached_or_noreserve is broken.  
>> I'm attaching two test cases to show this.  To run them, create a file 
>> hello.txt with one line, and then compile and run the two programs.
>>
>> They both take a region of size 128K and map the two halves of it 
>> separately with MAP_NORESERVE.  The only difference between the two 
>> cases is the order in which the halves are mapped.  They then call 
>> read(), passing the whole region of size 128K as buffer.  If 
>> mmap_is_attached_or_noreserve is working, the calls to read should 
>> succeed.  But only the one in mmap_noreserve2 succeeds, and the other 
>> fails with a bus error.  I'll now try to explain what's going on.
>>
>> The task is to find one or more noreserve records that completely 
>> cover the interval [addr, addr+len].
>>
>> After a successful call to match(start_addr, len, u_addr, u_len), we 
>> have the following situation:
>>
>>      |---------|---------------------|-----------------|
>> start_addr   u_addr          u_addr+u_len          start_addr+len
>>
>> The interval [u_addr, u_addr+u_len] is the intersection of 
>> [start_addr, start_addr+len] with the interval of the mmap_record.  
>> Note that u_addr  >= start_addr and u_addr+u_len <= start_addr+len, 
>> but I've shown in the picture the case where both inequalities are 
>> strict.
>>
>> At this point we know that the intersection is contained in a 
>> noreserve mmap_record.  But we commit the entire interval [start_addr, 
>> u_addr+u_len], whose length is commit_len, which is written as
>>
>>    u_len - (start_addr - u_addr) = (u_addr + u_len) - start_addr.
>>
>> There's no problem if u_addr = start_addr.  But if u_addr > 
>> start_addr, then the commit includes [start_addr, u_addr], which is 
>> not at this point known to be contained in a noreserve mmap_record.  
>> Moreover, even if it is contained in a noreserve record that we just 
>> haven't come to yet, experiments show that the call to VirtualAlloc in 
>> line 794 fails in this case.  Apparently you can't commit in one call 
>> to VirtualAlloc a region that was reserved [Windows terminology] in 
>> two separate calls.  I can't find this documented anywhere, but that's 
>> what happens in the attached mmap_noreserve1.c, as I verified by 
>> stepping through the code under gdb.
>>
>> As I said in an earlier message, I don't see how to fix this except by 
>> making sure that the noreserve maps in map_list occur in order of 
>> their starting address.  Then if we ever find u_addr > start_addr, we 
>> know we'll never find a noreserve mmap_record covering [start_addr, 
>> u_addr], and we can break out of the loop.  If you agree that there's 
>> a problem, does this seem like the right fix?
>>
>> I apologize for the length of this message, but I wanted to write 
>> everything out in detail in order to be clear.  I've written a patch 
>> (also attached) that attempts to do does what I suggested in the 
>> previous paragraph.  Unfortunately, when I install it, mintty hangs on 
>> startup without showing the bash prompt, and a grep process is left 
>> running.  So there must be something wrong with my patch, or else it 
>> triggers a latent bug somewhere else.
>>
>> Takashi, if you're reading this, could you take a look at my patch and 
>> see if you spot any obvious errors?  Or do you think this could be 
>> related to your recent changes?
> 
> I've find two errors, one in my patch and one in the original code: 
> There were two places in the main loop of mmap_is_attached_or_noreserve 
> where there was a "break" that should have been "continue".  Version 2 
> of the patch (attached) tries to fix these.  But I still get the same 
> mintty hang on startup.  So there's still something wrong.
> 
> Note that the original bug that I'm trying to fix caused SIGBUS to be 
> raised in some places where it shouldn't have been (due to a failed call 
> to VirtualAlloc).  The new break/continue bug I just found had the 
> opposite effect: it caused SIGBUS not to be raised in some places where 
> it should have been.
I've cleaned up and improved the patch, and I've sent it to 
cygwin-patches.  I'm stuck at this point.

Ken


More information about the Cygwin-developers mailing list