This is an archive of the discontinued LLVM Phabricator instance.

[libc++] Fix undefined behavior in `std::filebuf`
ClosedPublic

Authored by fwolff on Mar 22 2022, 1:36 PM.

Details

Summary

Attempts to fix #49267, #49282 and #49789.

Diff Detail

Event Timeline

fwolff created this revision.Mar 22 2022, 1:36 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 22 2022, 1:36 PM
fwolff requested review of this revision.Mar 22 2022, 1:36 PM
Herald added a reviewer: Restricted Project. · View Herald TranscriptMar 22 2022, 1:36 PM
ldionne requested changes to this revision.Mar 24 2022, 3:41 PM

Thanks for looking into this! I have some comments, but overall this looks sensible. I don't know this implementation very well, apologies if some of my comments/questions are naive.

libcxx/include/fstream
429–433

Suggestion (IMO that makes it clearer because we can see a pattern in how we initialize those):

ptrdiff_t __ln = __extbufnext_       ? __extbufnext_ - __extbuf_             : 0;
ptrdiff_t __le = __extbufend_        ? __extbufend_ - __extbuf_              : 0;
ptrdiff_t __rn = __rhs.__extbufnext_ ? __rhs.__extbufnext_ - __rhs.__extbuf_ : 0;
ptrdiff_t __re = __rhs.__extbufend_  ? __rhs.__extbufend_ - __rhs.__extbuf_  : 0;
433

I know nothing about this class' implementation and I haven't studied it deeply, but this is basically a small buffer optimization, right?

I'd add comments like this:

// *this uses the small buffer, __rhs doesn't
if (...) {

}
// *this doesn't use the small buffer, __rhs does
else if (...) {

}
// *this and __rhs both use the small buffer
else {

}

Actually, this makes me think -- aren't we missing the case where neither is using the small buffer?

450

We can use std:: for new code, no need to use _VSTD:: anymore!

libcxx/test/std/input.output/file.streams/fstreams/filebuf.assign/nonmember_swap_min.pass.cpp
2
12

Can you add a comment explaining what this tests, along with links to the fixed issues?

This revision now requires changes to proceed.Mar 24 2022, 3:41 PM
fwolff updated this revision to Diff 418315.Mar 25 2022, 1:35 PM
fwolff marked 5 inline comments as done.Mar 25 2022, 1:48 PM

Thanks for reviewing this @ldionne!

libcxx/include/fstream
433

I know nothing about this class' implementation and I haven't studied it deeply, but this is basically a small buffer optimization, right?

Right. I've added comments as you suggested.

Actually, this makes me think -- aren't we missing the case where neither is using the small buffer?

Good question, but this case is already handled by the outer if. I've added a comment there as well.

450

OK, thanks for the info. I was just "doing as the Romans do"...

libcxx/test/std/input.output/file.streams/fstreams/filebuf.assign/nonmember_swap_min.pass.cpp
2

Hm, I don't have a Windows machine where I could debug this, so I unfortunately can't fix this myself. But note that the assertion before the swap fails, so my changes can't have broken it, I think? I've disabled the test on Windows for now.

fwolff marked 3 inline comments as done.Mar 25 2022, 1:50 PM
ldionne added inline comments.Mar 28 2022, 12:00 PM
libcxx/test/std/input.output/file.streams/fstreams/filebuf.assign/nonmember_swap_min.pass.cpp
2

Could we also add this in libcxx/test/std/input.output/file.streams/fstreams/filebuf.assign/nonmember_swap.pass.cpp:

// lhs uses a small buffer, rhs is empty
{
    std::filebuf f;
    assert(f.open(temp.c_str(), std::ios_base::out | std::ios_base::in
                                            | std::ios_base::trunc) != 0);
    assert(f.is_open());
    assert(f.sputn("123", 3) == 3);
    f.pubseekoff(1, std::ios_base::beg);
    assert(f.sgetc() == '2');
    std::filebuf f2;
    swap(f, f2);
    assert(!f.is_open());
    assert(f2.is_open());
    assert(f2.sgetc() == '2');
}

This basically reverses the order of arguments to swap in the test that is already there. Perhaps we should also add a test that checks for swapping two filebufs that are not using the small buffer?

33

I don't think you ever use tmp, I think you could remove it altogether.

ldionne added inline comments.
libcxx/test/std/input.output/file.streams/fstreams/filebuf.assign/nonmember_swap_min.pass.cpp
13–15

Also, you should make this XFAIL: LIBCXX-WINDOWS-FIXME if you can't figure it out or reproduce, and @mstorsjo might be able to help.

mstorsjo added inline comments.Mar 28 2022, 12:26 PM
libcxx/test/std/input.output/file.streams/fstreams/filebuf.assign/nonmember_swap_min.pass.cpp
13–15

Don't make it LIBCXX-WINDOWS-FIXME, we don't want to add more of those. They were meant to only be used for the initial batch to get the testsuite working in such configs. We should be able to get rid of them and finally close https://github.com/llvm/llvm-project/issues/32077 real soon now, I'm just waiting for a CI runner upgrade)

A plain XFAIL with such a target triple (or just XFAIL: windows works too), with a FIXME comment, should be fine. But I can see if I can have a look at it...

mstorsjo added inline comments.Mar 28 2022, 2:05 PM
libcxx/test/std/input.output/file.streams/fstreams/filebuf.assign/nonmember_swap_min.pass.cpp
13–15

See D122612 for a fix for this issue.

D122612 is good to go, I think you should be able to rebase onto main soon, apply my remaining comments and then we can ship this!

fwolff updated this revision to Diff 421997.Apr 11 2022, 12:13 PM
fwolff marked 5 inline comments as done.

D122612 is good to go, I think you should be able to rebase onto main soon, apply my remaining comments and then we can ship this!

Great, thanks! I think I've addressed all comments.

ldionne accepted this revision.Apr 13 2022, 11:00 AM

Thanks!

This revision is now accepted and ready to land.Apr 13 2022, 11:00 AM
This revision was automatically updated to reflect the committed changes.