Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues found during tinyformat audit (part of Bitcoin Core audit) #70

Open
practicalswift opened this issue Jan 27, 2020 · 5 comments
Open

Comments

@practicalswift
Copy link

Hi all,

First, thanks for creating the tinyformat library. Having an easy-to-use locale independent formatting library available under a permissive license is really nice! :)

Bitcoin Core uses tinyformat for formatting of log messages. While auditing Bitcoin Core I discovered the following issues in tinyformat that I thought were worth reporting upstreams.

All issues have been verified against current master.

Issue 1. The following causes a signed integer overflow and a subsequent allocation of 9 GB of RAM (or an OOM in memory constrained environments):

tfm::format("%.777777700000000$", 1.0);

Issue 2. The following causes a stack overflow:

tfm::format("%987654321000000:", 1);

Issue 3. The following causes a stack overflow:

tfm::format("%1$*1$*", -11111111);

Issue 4. The following causes a NULL pointer dereference:

tfm::format("%.1s", (char *)nullptr);

Issue 5. The following causes a float cast overflow:

tfm::format("%c", -1000.0);

Issue 6. The following causes a float cast overflow followed by an invalid integer negation:

tfm::format("%*", std::numeric_limits<double>::lowest());

Note that I've only audited tfm::format(…, …) which is the part of tinyformat used by Bitcoin Core.

Thanks for a nice library!

@c42f
Copy link
Owner

c42f commented Jan 28, 2020

Hi, thanks for this review! I'd certainly like to fix any of these which reasonably can be fixed or mitigated. Can you describe the types of programming errors or attacks that Bitcoin Core is trying to mitigate against?

I ask because I feel like there's a few classes of possible error and they are not equivalent:

  1. Programmer error: Something invalid is passed to tfm::format. For example, issue 4 where a null pointer is passed.
  2. User attack: The user is able to influence the data passed to tfm::format
    2.1: Users are allowed set the args passed to tfm::format(fmtspec, args...)
    2.2: Users are allowed to set fmtspec in tfm::format(fmtspec, args...).

Mitigating programmer error is only ever going to be a best-effort, for example tfm::format("%s", (char *)1) will segfault but this invalid usage cannot be detected by tfm::format. I'm happy to mitigate issue 4 (null pointer dereference) because it's both common and detectable but in general being printf compatible means accepting bare pointers which we cannot validate.

I feel tinyformat should defend against user attacks of type 2.1. Cases where the user can crash the program by providing out-of-domain args for a fixed, valid fmtspec seems like a bug.

I'm not yet sure it's even possible to defend effectively against 2.2 where users have access to fmtspec. For example, tfm::format(fmtspec, (char*)1) is valid when fmtspec="%p" but a segfault when fmtspec="%s". I've always thought that giving the user access to fmtspec is fundamentally unsafe and a bad idea (it certainly is with C's printf). It may be possible to make safe in tinyformat with some work but I currently don't recommend it. What are Bitcoin Core's expectations about this?

@practicalswift
Copy link
Author

@c42f

I cannot speak for Bitcoin Core, but for me personally the reason I like tinyformat is that it is more robust against malformed input compared to the alternatives.

If we can make it more robust I'll like it even more :)

I really like robustness. Ideally (and if possible) both in terms of robustness against programmer error and in terms of robustness against user attack. In some cases this ideal state is not possible: as you note the invalid pointer case cannot be guarded against.

AFAICT the maximally robust version for tfm::format would have a contract along the lines of:

Assuming that arg in tfm::format(fmtspec, arg) is not an invalid pointer
then the function is guaranteed to a.) return a formatted std::string, or
b.) TINYFORMAT_ERROR(…) will be called.

As a user of the library I would love such a contract as it is maximally robust against crazy input (regardless of the reason for said crazy input) :)

Besides the invalid pointer case, can we think of any failure scenario that is technically impossible to guard against?

@maflcko
Copy link

maflcko commented Jan 28, 2020

In Bitcoin Core, all format strings are hardcoded and I don't think we pass in pointers in the args, so our usage should be fine.

@practicalswift
Copy link
Author

practicalswift commented Jan 28, 2020

@MarcoFalke Under the assumption of no programmer mistakes, yes. It would be strictly better if we had robustness also in the presence of programmer mistakes (which is entirely possible with the notable exception of invalid pointers mentioned above), no? Robustness is an undervalued property :)

maflcko pushed a commit to bitcoin/bitcoin that referenced this issue Jan 30, 2020
cc668d0 tests: Add fuzzing harness for strprintf(...) (practicalswift)
ccc3c76 tests: Add fuzzer strprintf to FUZZERS_MISSING_CORPORA (temporarily) (practicalswift)
6ef0491 tests: Update FuzzedDataProvider.h from upstream (LLVM) (practicalswift)

Pull request description:

  Add fuzzing harness for `strprintf(…)`.

  Update `FuzzedDataProvider.h`.

  Avoid hitting some issues in tinyformat (reported upstreams in c42f/tinyformat#70).

  ---

  Found issues in tinyformat:

  **Issue 1.** The following causes a signed integer overflow followed by an allocation of 9 GB of RAM (or an OOM in memory constrained environments):

  ```
  strprintf("%.777777700000000$", 1.0);
  ```

  **Issue 2.** The following causes a stack overflow:

  ```
  strprintf("%987654321000000:", 1);
  ```

  **Issue 3.** The following causes a stack overflow:

  ```
  strprintf("%1$*1$*", -11111111);
  ```

  **Issue 4.** The following causes a `NULL` pointer dereference:

  ```
  strprintf("%.1s", (char *)nullptr);
  ```

  **Issue 5.** The following causes a float cast overflow:

  ```
  strprintf("%c", -1000.0);
  ```

  **Issue 6.** The following causes a float cast overflow followed by an invalid integer negation:

  ```
  strprintf("%*", std::numeric_limits<double>::lowest());
  ```

Top commit has no ACKs.

Tree-SHA512: 9b765559281470f4983eb5aeca94bab1b15ec9837c0ee01a20f4348e9335e4ee4e4fecbd7a1a5a8ac96aabe0f9eeb597b8fc9a2c8faf1bab386e8225d5cdbc18
@practicalswift
Copy link
Author

practicalswift commented Feb 10, 2020

Some additional minor issues found:

strprintf("%c", 1.31783e+38);
tinyformat.h:244:36: runtime error: 1.31783e+38 is outside the range of representable values of type 'char'
strprintf("%c", 2.54176e+307);
tinyformat.h:244:36: runtime error: 2.54176e+307 is outside the range of representable values of type 'char'

strprintf("%*", -2.33527e+38);
tinyformat.h:283:65: runtime error: -2.33527e+38 is outside the range of representable values of type 'int'
strprintf("%*", -1.45258e+308);
tinyformat.h:283:65: runtime error: -1.45258e+308 is outside the range of representable values of type 'int'
strprintf("%*", -2147483648);
tinyformat.h:763:25: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself

vasild added a commit to vasild/bitcoin that referenced this issue Jul 16, 2020
jasonbcox pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this issue Oct 20, 2020
Summary:
```
Add fuzzing harness for strprintf(…).

Update FuzzedDataProvider.h.

Avoid hitting some issues in tinyformat (reported upstreams in
c42f/tinyformat#70).
```

Backport of core [[bitcoin/bitcoin#18009 | PR18009]].

Test Plan:
  ninja bitcoin-fuzzers
  ./src/test/fuzz/strprintf

Reviewers: #bitcoin_abc, deadalnix

Reviewed By: #bitcoin_abc, deadalnix

Differential Revision: https://reviews.bitcoinabc.org/D8004
laanwj added a commit to bitcoin-core/gui that referenced this issue May 10, 2021
…re known to fail

facfc0f fuzz: Remove strprintf test cases that are known to fail (MarcoFalke)

Pull request description:

  They are still waiting to be fixed (see c42f/tinyformat#70 ), so no need for us to carry them around in our source code. They can be added back once upstream is fixed.

  Hopefully fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=34082

ACKs for top commit:
  laanwj:
    Code review ACK facfc0f

Tree-SHA512: d9d3d35555b6d58740a041ae45797ca85149f60990e2ed632c5dadf363e1d2362d2447681d7ceaa1fbffcd6e7bc8da5bc15d3923b68829a86c25b364a599afc8
sidhujag pushed a commit to syscoin/syscoin that referenced this issue May 10, 2021
… to fail

facfc0f fuzz: Remove strprintf test cases that are known to fail (MarcoFalke)

Pull request description:

  They are still waiting to be fixed (see c42f/tinyformat#70 ), so no need for us to carry them around in our source code. They can be added back once upstream is fixed.

  Hopefully fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=34082

ACKs for top commit:
  laanwj:
    Code review ACK facfc0f

Tree-SHA512: d9d3d35555b6d58740a041ae45797ca85149f60990e2ed632c5dadf363e1d2362d2447681d7ceaa1fbffcd6e7bc8da5bc15d3923b68829a86c25b364a599afc8
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this issue Apr 10, 2023
… to fail

facfc0f fuzz: Remove strprintf test cases that are known to fail (MarcoFalke)

Pull request description:

  They are still waiting to be fixed (see c42f/tinyformat#70 ), so no need for us to carry them around in our source code. They can be added back once upstream is fixed.

  Hopefully fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=34082

ACKs for top commit:
  laanwj:
    Code review ACK facfc0f

Tree-SHA512: d9d3d35555b6d58740a041ae45797ca85149f60990e2ed632c5dadf363e1d2362d2447681d7ceaa1fbffcd6e7bc8da5bc15d3923b68829a86c25b364a599afc8
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this issue Apr 15, 2023
… to fail

facfc0f fuzz: Remove strprintf test cases that are known to fail (MarcoFalke)

Pull request description:

  They are still waiting to be fixed (see c42f/tinyformat#70 ), so no need for us to carry them around in our source code. They can be added back once upstream is fixed.

  Hopefully fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=34082

ACKs for top commit:
  laanwj:
    Code review ACK facfc0f

Tree-SHA512: d9d3d35555b6d58740a041ae45797ca85149f60990e2ed632c5dadf363e1d2362d2447681d7ceaa1fbffcd6e7bc8da5bc15d3923b68829a86c25b364a599afc8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants