On Friday, May 29, 2015, Alexey Samsonov <<a href="mailto:vonosmas@gmail.com">vonosmas@gmail.com</a>> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Hi everyone,<div><br></div><div>Having proper error handling in LLVM's Object parsing library is a nice thing by itself, but it would additionally allow us to find bugs by fuzzing (see r238451 that adds llvm-dwarfdump-fuzzer tool), for which the clean input validation is essential.</div><div><br></div><div>This is a generic discussion of state of affairs. I want to do some progress in fuzzing before we finish it (especially if we decide to make a significant intrusive changes), you may scroll down for my plan.</div><div><br></div><div>The code in lib/Object calls report_fatal_error() far too often, both when we're (a) just constructing the specific implementation of ObjectFile, and (b) when we access its contents and find out the file is broken and can't be parsed properly.</div><div><br></div><div>We should just go and fix (a): ObjectFile factory methods return ErrorOr<std::unique_ptr<ObjectFile>>, and we should propagate the error appropriately.</div><div><br></div><div>(b) is harder. The current approach is to use std::error_code as a return type, and store the result in by-reference argument, for instance:</div><div>  std::error_code getSymbolAddress(DataRefImpl Symbol, uint64_t &Res);</div><div><br></div><div>I wanted to follow this approach in a proposed large MachO API change</div><div>(<a href="https://urldefense.proofpoint.com/v2/url?u=http-3A__reviews.llvm.org_D10111&d=AwMFaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=Mfk2qtn1LTDThVkh6-oGglNfMADXfJdty4_bhmuhMHA&m=t8fOzjcE-HffAHdlkx8x2RzDYXmOrfwTVRZMBY_I1-k&s=vIEUEH66ZENYUbm5_hrLkGBXJih9kgG91w9SNPrpGGU&e=" target="_blank">http://reviews.llvm.org/D10111</a>), but it raised discussion on whether this approach is right.</div><div>Moving this discussion here. I see the following options:</div><div><br></div><div>1. Use the current approach:</div><div>  std::error_code getSymbolAddress(DataRefImpl Symbol, uint64_t &Res);</div><div><br></div><div>Pros:</div><div>  * don't need to change a large number of (often virtual) API functions</div><div>  * has a nice error handling pattern used in LLVM tools:</div><div>  uint64_t Addr;</div><div>  if (error(getSymbolAddress(Symbol, Addr)))</div><div>    return;  // or continue, or do anything else.</div><div><br></div><div>Cons:</div><div>  * return value can just be silently ignored. Adding warn_unused_result attribute on per-function basis is ugly</div><div>  * adds extra overhead for cases when we're certain the result would be valid.</div><div><br></div><div>2. Switch to ErrorOr wrapper:</div><div>  ErrorOr<uint64_t> getSymbolAddress(DataRefImpl Symbol);</div><div><br></div><div>Pros:</div><div>  * handling the error is now mandatory and explicit.</div><div>  * callers can explicitly skip error handling if they know the result would be valid:</div><div>    uint64_t Addr = getSymbolAddress(Symbol).get();</div><div>  and it would fail the assert if they are wrong.</div><div><br></div><div>Cons:</div><div>  * need to change lots of old code, or live with two versions of functions</div><div>  * error handling boilerplate in regular code on call site is ugly:</div><div>  auto AddrOrErr = getSymbolAddress(Symbol);</div><div>  if (AddrOrErr.hasError())</div><div>    return;  // or continue, or whatever</div><div>  uint64_t Addr = AddrOrErr.get();</div><div>  (can probably be improved with a macro)</div><div>  * adds extra overhead for cases when we're certain the result would be valid.</div></div></blockquote><div><br></div><div>The "error handling boilerplate" here isn't actually all that bad, and ErrorOr can generally just be used like an API that returns a pointer and null on err, but with better errors, ie:<br></div><div><br></div><div>  auto Addr = getSymbolAddress(...)</div><div>  if (auto EC = Addr.getError()) {</div><div>    // handle the error</div><div>  }</div><div>  // use *Addr via dereference</div><div><br></div><div>This is the same number of lines of code a (1), but harder to misuse. There are situations where it's not quite this nice, like ErrorOr<unique_ptr>, but that's worth solving in its own right if this is an issue.</div><div><br></div>I haven't looked too much at the code in question, but I'm pretty confident that this is the right way to go about it.<br><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div>On IRC discussion Lang suggested</div><div>3. Check the whole object file contents in constructor or validate() method, and get rid</div><div>of all error codes in regular accessors.</div><div><br></div><div>Pros:</div><div>  * the interface is much cleaner</div><div>  * no extra overhead for trusted (e.g. JIT) object files.</div><div><br></div><div>Cons:</div><div>  * significant change, fundamentally shifting the way object files are handled</div><div>  * verifier function should now absolutely everything about the object file, and anticipate all possible use cases. Especially hard, assuming that ObjectFile interface allows user to pass any garbage as input arguments (e.g. as DataRefImpl in the example above).</div><div>  * verifier can be slow, and might be an overkill if we strongly desire to parse some bits of object file lazily.</div><div><br></div><div>================</div><div><br></div><div>Instead of <a href="https://urldefense.proofpoint.com/v2/url?u=http-3A__reviews.llvm.org_D10111&d=AwMFaQ&c=8hUWFZcy2Z-Za5rBPlktOQ&r=Mfk2qtn1LTDThVkh6-oGglNfMADXfJdty4_bhmuhMHA&m=t8fOzjcE-HffAHdlkx8x2RzDYXmOrfwTVRZMBY_I1-k&s=vIEUEH66ZENYUbm5_hrLkGBXJih9kgG91w9SNPrpGGU&e=" target="_blank">http://reviews.llvm.org/D10111</a>, I'm going to proceed with minimal incremental changes, that would allow fuzzer to move forward. Namely, I want to keep the changes to headers as small as possible, changing functions one by one, and preferring to use ErrorOr<> construct (option 2 listed above). An additional benefit of this is that each small incremental change would be accompanied by the test case generated by fuzzer, that exposed this problem.</div><div><br></div><div>Let me know if you think it's a good or terrible idea.</div><div><br></div><div>-- <br></div><div><div><div><div><div dir="ltr">Alexey Samsonov<br><a href="javascript:_e(%7B%7D,'cvml','vonosmas@gmail.com');" target="_blank">vonosmas@gmail.com</a></div></div>
</div></div></div></div>
</blockquote>