Skip to content

Conversation

@fanquake
Copy link
Member

@fanquake fanquake commented Mar 8, 2020

This is a slightly belated follow up to #17686 and some discussion with Cory. It's not entirely clear if we should make this change due to the way the macOS dynamic loader appears to work. However I'm opening this for some discussion. Also related to #17768.

Issue:

LD64 doesn't set the MH_BINDATLOAD bit in the header of MACHO executables, when building with -bind_at_load. This is in contradiction to the documentation:

-bind_at_load
     Sets a bit in the mach header of the resulting binary which tells dyld to
     bind all symbols when the binary is loaded, rather than lazily.

The ld in Apples cctools does set the bit, however the cctools-port that we use for release builds, bundles LD64.

However; even if the linker hasn't set that bit, the dynamic loader (dyld) doesn't seem to ever check for it, and from what I understand, it looks at a different part of the header when determining whether to lazily load symbols.

Note that our release binaries are currently working as expected, and no lazy loading occurs.

Example:

Using a small program, we can observe the behaviour of the dynamic loader.

Conducted using:

clang++ --version
Apple clang version 11.0.0 (clang-1100.0.33.17)
Target: x86_64-apple-darwin18.7.0

ld -v
@(#)PROGRAM:ld  PROJECT:ld64-530
BUILD 18:57:17 Dec 13 2019
LTO support using: LLVM version 11.0.0, (clang-1100.0.33.17) (static support for 23, runtime is 23)
TAPI support using: Apple TAPI version 11.0.0 (tapi-1100.0.11)
#include <iostream>
int main() {
	std::cout << "Hello World!\n";
	return 0;
}

Compile and check the MACHO header:

clang++ test.cpp -o test
otool -vh test
...
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL LIB64     EXECUTE    16       1424   NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE

# Run and dump dynamic loader bindings:
DYLD_PRINT_BINDINGS=1 DYLD_PRINT_TO_FILE=no_bind.txt ./test
Hello World!

Recompile with -bind_at_load. Note still no BINDATLOAD flag:

clang++ test.cpp -o test -Wl,-bind_at_load
otool -vh test
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL LIB64     EXECUTE    16       1424   NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE
...
DYLD_PRINT_BINDINGS=1 DYLD_PRINT_TO_FILE=bind.txt ./test
Hello World!

If we diff the outputs, you can see that dyld doesn't perform any lazy bindings when the binary is compiled with -bind_at_load, even if the BINDATLOAD flag is not set:

@@ -1,11 +1,27 @@
+dyld: bind: test:0x103EDF030 = libc++.1.dylib:__ZNKSt3__16locale9use_facetERNS0_2idE, *0x103EDF030 = 0x7FFF70C9FA58
+dyld: bind: test:0x103EDF038 = libc++.1.dylib:__ZNKSt3__18ios_base6getlocEv, *0x103EDF038 = 0x7FFF70CA12C2
+dyld: bind: test:0x103EDF068 = libc++.1.dylib:__ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC1ERS3_, *0x103EDF068 = 0x7FFF70CA12B6
+dyld: bind: test:0x103EDF070 = libc++.1.dylib:__ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev, *0x103EDF070 = 0x7FFF70CA1528
+dyld: bind: test:0x103EDF080 = libc++.1.dylib:__ZNSt3__16localeD1Ev, *0x103EDF080 = 0x7FFF70C9FAE6
<trim>
-dyld: lazy bind: test:0x10D4AC0C8 = libsystem_platform.dylib:_strlen, *0x10D4AC0C8 = 0x7FFF73C5C6E0
-dyld: lazy bind: test:0x10D4AC068 = libc++.1.dylib:__ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC1ERS3_, *0x10D4AC068 = 0x7FFF70CA12B6
-dyld: lazy bind: test:0x10D4AC038 = libc++.1.dylib:__ZNKSt3__18ios_base6getlocEv, *0x10D4AC038 = 0x7FFF70CA12C2
-dyld: lazy bind: test:0x10D4AC030 = libc++.1.dylib:__ZNKSt3__16locale9use_facetERNS0_2idE, *0x10D4AC030 = 0x7FFF70C9FA58
-dyld: lazy bind: test:0x10D4AC080 = libc++.1.dylib:__ZNSt3__16localeD1Ev, *0x10D4AC080 = 0x7FFF70C9FAE6
-dyld: lazy bind: test:0x10D4AC070 = libc++.1.dylib:__ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev, *0x10D4AC070 = 0x7FFF70CA1528

Note: dyld also has a DYLD_BIND_AT_LAUNCH=1 environment variable, that when set, will force any lazy bindings to be non-lazy:

dyld: forced lazy bind: test:0x10BEC8068 = libc++.1.dylib:__ZNSt3__113basic_ostream

Thoughts:

After looking at the dyld source, I can't find any checks for MH_BINDATLOAD. You can see the flags it does check for, such as MH_PIE or MH_BIND_TO_WEAK here.

It seems that the lazy binding of any symbols depends on whether or not lazy_bind_size from the LC_DYLD_INFO_ONLY load command is > 0. Which was mentioned in #17686.

Changes:

This PR is one of Corys commits, that I've rebased and modified to make build. I've also included an addition to the security-check.py script to check for the flag.

However, given the above, I'm not entirely sure this patch is the correct approach. If the linker no-longer inserts it, and the dynamic loader doesn't look for it, there might be little benefit to setting it. Or, maybe this is an oversight from Apple and needs some upstream discussion. Looking for some thoughts / Concept ACK/NACK.

One alternate approach we could take is to drop the patch and modify security-check.py to look for lazy_bind_size == 0 in the LC_DYLD_INFO_ONLY load command, using otool -l.

@laanwj
Copy link
Member

laanwj commented Mar 10, 2020

@fanquake and me had some discussion about this on IRC. The conclusion was that it's probably better to ignore the MH_BINDATLOAD flag like MacOS' static linker does. Instead, to adopt the security check for the change(s) that -Wl,-bind_at_load actually makes and is used.

Also, it would make sense to get Apple to clarify the documentation on this. It happens more often that header bits simply go unused in new versions in favor of new mechanisms, but it would have avoided a lot of hassle if this had been properly documented.

@fanquake
Copy link
Member Author

Also, it would make sense to get Apple to clarify the documentation on this

I've reached out to a few people at Apple. So hopefully we'll get some clarification.

@laanwj
Copy link
Member

laanwj commented Mar 11, 2020

Great!

@fanquake
Copy link
Member Author

Received this reply from Nick Kledzik (ld / dyld at Apple):

Michael,

Traditionally, there is two kinds of binding. Binds which must be done at load time before any code is run, and binds which can be done lazily. Lazy binds are only used for procedural interfaces. That is, the symbol is an external function call. The linker tool and dyld conspire to have lazying bind work by having the actual pointer bound to a helper which on first call looks up and set the pointer to the real implementation. This lazy symbol optimization made sense if looking up symbols took a long time and the program was not going to call them immediately. With dyld3 all symbol lookups are done ahead of time and cached, so lazy bind makes no sense.

The semantics of the -bind_at_load option is omit that optimization. That is, have all binding done at launch. Originally that was done by setting the MH_BINDATLOAD bit in the mach_header and have dyld bind all lazy pointer early.

A few years back we changed to instead have the static linker generate the binary without lazy binding - just up front binding for all symbols. You can see that in this example:

# regular build (has lazy binding)
[/tmp]> xcrun -sdk macosx.internal clang main.c
[/tmp]> xcrun dyldinfo -lazy_bind -bind a.out
bind information:
segment section          address        type    addend dylib            symbol
__DATA_CONST __got            0x100001000    pointer      0 libSystem        dyld_stub_binder
lazy binding information (from lazy_bind part of dyld info):
segment section          address    index  dylib                        symbol
__DATA       __la_symbol_ptr  0x100002000 0x0000           libSystem        _printf
# -bind_at_load build (no lazy binding)
[/tmp]> xcrun -sdk macosx.internal clang main.c -bind_at_load
[/tmp]> xcrun dyldinfo -lazy_bind -bind a.out
bind information:
segment section          address        type    addend dylib              symbol
__DATA       __la_symbol_ptr  0x100002000    pointer      0 libSystem        _printf
__DATA_CONST __got            0x100001000    pointer      0 libSystem        dyld_stub_binder
no compressed lazy binding info

@laanwj
Copy link
Member

laanwj commented Mar 25, 2020

Thanks for asking for clarification.

So from what I understand essentially this means that there is no point in forcing MH_BINDATLOAD because it's no longer necessary?

I think this means we don't need this and can close it?

@DrahtBot
Copy link
Contributor

DrahtBot commented Mar 26, 2020

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

No conflicts as of last run.

@DrahtBot
Copy link
Contributor

🐙 This pull request conflicts with the target branch and needs rebase.

@fanquake
Copy link
Member Author

I think this means we don't need this and can close it?

I've asked Cory to follow up here now that we have some more information. I think even if we didn't want to use MH_BINDATLOAD we can at least add a related check to security-check.py.

@fanquake fanquake force-pushed the does_noone_care_about_MH_BINDATLOAD_anymore branch from e72f8da to 5884bcd Compare March 30, 2020 02:39
@theuni
Copy link
Member

theuni commented Apr 3, 2020

@fanquake Thanks for pinging someone at Apple!

I think I understand now, and that makes sense. I wasn't aware of the old faux-static linkage, but the flag makes sense in that context. Sounds like it produced the same behavior that a normal link and DYLD_BIND_AT_LAUNCH at runtime would now.

That confirms (to me, anyway) that we're doing the right (extra paranoid) thing by turning on bind_at_load and sacrificing startup time to avoid stub binding that obfuscates what's happening at runtime, which proved to be a real issue recently: bitcoin-core/secp256k1#674 (comment)

It also explains why the dyloader doesn't bother checking for the flag at runtime, so there's no need for us to set the bit.

It also confirms that your thoughts on checks in #17686 were spot on:

"You can check the binaries using otool -l, and looking for the LC_DYLD_INFO_ONLY section;
lazy_bind_off and lazy_bind_size should both be 0."

So, let's go for that instead, unless it's already been merged as part of another PR?

Kudos to @fanquake for looking so deeply into this, this was some really great detective work.

@fanquake fanquake force-pushed the does_noone_care_about_MH_BINDATLOAD_anymore branch from 5884bcd to 153dba1 Compare April 4, 2020 01:52
@fanquake fanquake changed the title build: teach ld64 to actually insert MH_BINDATLOAD scripts: add MACHO BINDATLOAD check to security-check.py Apr 4, 2020
@fanquake fanquake changed the title scripts: add MACHO BINDATLOAD check to security-check.py scripts: add MACHO lazy bindings check to security-check.py Apr 4, 2020
@fanquake fanquake force-pushed the does_noone_care_about_MH_BINDATLOAD_anymore branch from 153dba1 to 5ca90f8 Compare April 4, 2020 01:54
@fanquake
Copy link
Member Author

fanquake commented Apr 4, 2020

So, let's go for that instead,

Thanks for following up. I've added a different test to security-check.py, which checks the otool -l output, and dropped the other changes.

@theuni
Copy link
Member

theuni commented Apr 8, 2020

ACK 5ca90f8

@bitcoin bitcoin deleted a comment from DrahtBot Apr 8, 2020
@DrahtBot
Copy link
Contributor

DrahtBot commented Apr 9, 2020

Gitian builds

File commit 2392566
(master)
commit 4fe1a3c9d04b46f08f70b74a8567b728f31927fc
(master and this pull)
bitcoin-0.19.99-aarch64-linux-gnu-debug.tar.gz 828cc7165fcda6d7... f3b9690c517d9059...
bitcoin-0.19.99-aarch64-linux-gnu.tar.gz 19f39430f6d6b932... 86ca6c57c5ec6fc1...
bitcoin-0.19.99-arm-linux-gnueabihf-debug.tar.gz b286dc1cfdd27aba... 53ba6e0e8f9ac2cc...
bitcoin-0.19.99-arm-linux-gnueabihf.tar.gz 2e878c97fd9f751f... 36469b72a4662acf...
bitcoin-0.19.99-osx-unsigned.dmg ba4c6e4dfe5f1a44... eb2f85206862b352...
bitcoin-0.19.99-osx64.tar.gz 7d187bef777061e0... 0b7d7c09764b938c...
bitcoin-0.19.99-riscv64-linux-gnu-debug.tar.gz 9a7450aa0ee7e771... c4b9d5f0722a3ace...
bitcoin-0.19.99-riscv64-linux-gnu.tar.gz 164a69c7fdb34e11... 09adf866d4f66fd0...
bitcoin-0.19.99-win64-debug.zip 5d8182cff04f0b23... 073839c45a9136e1...
bitcoin-0.19.99-win64-setup-unsigned.exe 64a56adc8f69e4c6... 107383e299f03bb5...
bitcoin-0.19.99-win64.zip be63697d003bbb92... f8dc05a3eb0d6c5d...
bitcoin-0.19.99-x86_64-linux-gnu-debug.tar.gz 298322ed9ef88bec... bdc52155fa70b17b...
bitcoin-0.19.99-x86_64-linux-gnu.tar.gz b71bfcf87e8e358d... e4570cd79dc22e31...
bitcoin-0.19.99.tar.gz 7d4b6a6e5c51268e... f27925bee4954a49...
bitcoin-core-linux-0.20-res.yml f88a03e78fc38919... 7561725e03990ebe...
bitcoin-core-osx-0.20-res.yml 7c8b45a96259f8b1... f81dbafcc120c4be...
bitcoin-core-win-0.20-res.yml d16c64f2db26cf2a... f93c8a58b1923457...
linux-build.log ff9228e47e8fb766... 8c5b55e1802f6308...
osx-build.log f925455d7374a55b... fb1ad644fa0d6a84...
win-build.log 4d8c4d669d323e8c... 7480254e38a93b0e...
bitcoin-core-linux-0.20-res.yml.diff 109a108b45090280...
bitcoin-core-osx-0.20-res.yml.diff 14e31aab16dd39a8...
bitcoin-core-win-0.20-res.yml.diff 0ce086b52363f070...
linux-build.log.diff a4202579b5719f74...
osx-build.log.diff 47652bfb05a2dd88...
win-build.log.diff 8491793429c1912e...

@fanquake fanquake merged commit d486991 into bitcoin:master Apr 9, 2020
@fanquake fanquake deleted the does_noone_care_about_MH_BINDATLOAD_anymore branch April 9, 2020 23:43
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Apr 13, 2020
…ty-check.py

5ca90f8 scripts: add MACHO lazy bindings check to security-check.py (fanquake)

Pull request description:

  This is a slightly belated follow up to bitcoin#17686 and some discussion with Cory. It's not entirely clear if we should make this change due to the way the macOS dynamic loader appears to work. However I'm opening this for some discussion. Also related to bitcoin#17768.

  #### Issue:
  [`LD64`](https://opensource.apple.com/source/ld64/) doesn't set the [MH_BINDATLOAD](https://opensource.apple.com/source/xnu/xnu-6153.11.26/EXTERNAL_HEADERS/mach-o/loader.h.auto.html) bit in the header of MACHO executables, when building with `-bind_at_load`. This is in contradiction to the [documentation](https://opensource.apple.com/source/ld64/ld64-450.3/doc/man/man1/ld.1.auto.html):
  ```bash
  -bind_at_load
       Sets a bit in the mach header of the resulting binary which tells dyld to
       bind all symbols when the binary is loaded, rather than lazily.
  ```

  The [`ld` in Apples cctools](https://opensource.apple.com/source/cctools/cctools-927.0.2/ld/layout.c.auto.html) does set the bit, however the [cctools-port](https://github.com/tpoechtrager/cctools-port/) that we use for release builds, bundles `LD64`.

  However; even if the linker hasn't set that bit, the dynamic loader ([`dyld`](https://opensource.apple.com/source/dyld/)) doesn't seem to ever check for it, and from what I understand, it looks at a different part of the header when determining whether to lazily load symbols.

  Note that our release binaries are currently working as expected, and no lazy loading occurs.

  #### Example:

  Using a small program, we can observe the behaviour of the dynamic loader.

  Conducted using:
  ```bash
  clang++ --version
  Apple clang version 11.0.0 (clang-1100.0.33.17)
  Target: x86_64-apple-darwin18.7.0

  ld -v
  @(#)PROGRAM:ld  PROJECT:ld64-530
  BUILD 18:57:17 Dec 13 2019
  LTO support using: LLVM version 11.0.0, (clang-1100.0.33.17) (static support for 23, runtime is 23)
  TAPI support using: Apple TAPI version 11.0.0 (tapi-1100.0.11)
  ```

  ```cpp
  #include <iostream>
  int main() {
  	std::cout << "Hello World!\n";
  	return 0;
  }
  ```

  Compile and check the MACHO header:
  ```bash
  clang++ test.cpp -o test
  otool -vh test
  ...
  Mach header
        magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
  MH_MAGIC_64  X86_64        ALL LIB64     EXECUTE    16       1424   NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE

  # Run and dump dynamic loader bindings:
  DYLD_PRINT_BINDINGS=1 DYLD_PRINT_TO_FILE=no_bind.txt ./test
  Hello World!
  ```

  Recompile with `-bind_at_load`. Note still no `BINDATLOAD` flag:
  ```bash
  clang++ test.cpp -o test -Wl,-bind_at_load
  otool -vh test
  Mach header
        magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
  MH_MAGIC_64  X86_64        ALL LIB64     EXECUTE    16       1424   NOUNDEFS DYLDLINK TWOLEVEL WEAK_DEFINES BINDS_TO_WEAK PIE
  ...
  DYLD_PRINT_BINDINGS=1 DYLD_PRINT_TO_FILE=bind.txt ./test
  Hello World!
  ```

  If we diff the outputs, you can see that `dyld` doesn't perform any lazy bindings when the binary is compiled with `-bind_at_load`, even if the `BINDATLOAD` flag is not set:
  ```diff
  @@ -1,11 +1,27 @@
  +dyld: bind: test:0x103EDF030 = libc++.1.dylib:__ZNKSt3__16locale9use_facetERNS0_2idE, *0x103EDF030 = 0x7FFF70C9FA58
  +dyld: bind: test:0x103EDF038 = libc++.1.dylib:__ZNKSt3__18ios_base6getlocEv, *0x103EDF038 = 0x7FFF70CA12C2
  +dyld: bind: test:0x103EDF068 = libc++.1.dylib:__ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC1ERS3_, *0x103EDF068 = 0x7FFF70CA12B6
  +dyld: bind: test:0x103EDF070 = libc++.1.dylib:__ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev, *0x103EDF070 = 0x7FFF70CA1528
  +dyld: bind: test:0x103EDF080 = libc++.1.dylib:__ZNSt3__16localeD1Ev, *0x103EDF080 = 0x7FFF70C9FAE6
  <trim>
  -dyld: lazy bind: test:0x10D4AC0C8 = libsystem_platform.dylib:_strlen, *0x10D4AC0C8 = 0x7FFF73C5C6E0
  -dyld: lazy bind: test:0x10D4AC068 = libc++.1.dylib:__ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC1ERS3_, *0x10D4AC068 = 0x7FFF70CA12B6
  -dyld: lazy bind: test:0x10D4AC038 = libc++.1.dylib:__ZNKSt3__18ios_base6getlocEv, *0x10D4AC038 = 0x7FFF70CA12C2
  -dyld: lazy bind: test:0x10D4AC030 = libc++.1.dylib:__ZNKSt3__16locale9use_facetERNS0_2idE, *0x10D4AC030 = 0x7FFF70C9FA58
  -dyld: lazy bind: test:0x10D4AC080 = libc++.1.dylib:__ZNSt3__16localeD1Ev, *0x10D4AC080 = 0x7FFF70C9FAE6
  -dyld: lazy bind: test:0x10D4AC070 = libc++.1.dylib:__ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev, *0x10D4AC070 = 0x7FFF70CA1528
  ```

  Note: `dyld` also has a `DYLD_BIND_AT_LAUNCH=1` environment variable, that when set, will force any lazy bindings to be non-lazy:
  ```bash
  dyld: forced lazy bind: test:0x10BEC8068 = libc++.1.dylib:__ZNSt3__113basic_ostream
  ```

  #### Thoughts:
  After looking at the dyld source, I can't find any checks for `MH_BINDATLOAD`. You can see the flags it does check for, such as MH_PIE or MH_BIND_TO_WEAK [here](https://opensource.apple.com/source/dyld/dyld-732.8/src/ImageLoaderMachO.cpp.auto.html).

  It seems that the lazy binding of any symbols depends on whether or not [lazy_bind_size](https://opensource.apple.com/source/xnu/xnu-6153.11.26/EXTERNAL_HEADERS/mach-o/loader.h.auto.html) from the `LC_DYLD_INFO_ONLY` load command is > 0. Which was mentioned in [bitcoin#17686](bitcoin#17686 (comment)).

  #### Changes:
  This PR is one of [Corys commits](theuni@7b6ba26), that I've rebased and modified to make build. I've also included an addition to the `security-check.py` script to check for the flag.

  However, given the above, I'm not entirely sure this patch is the correct approach. If the linker no-longer inserts it, and the dynamic loader doesn't look for it, there might be little benefit to setting it. Or, maybe this is an oversight from Apple and needs some upstream discussion. Looking for some thoughts / Concept ACK/NACK.

  One alternate approach we could take is to drop the patch and modify security-check.py to look for `lazy_bind_size` == 0 in the `LC_DYLD_INFO_ONLY` load command, using `otool -l`.

ACKs for top commit:
  theuni:
    ACK 5ca90f8

Tree-SHA512: 444022ea9d19ed74dd06dc2ab3857a9c23fbc2f6475364e8552d761b712d684b3a7114d144f20de42328d1a99403b48667ba96885121392affb2e05b834b6e1c
fanquake added a commit to fanquake/bitcoin that referenced this pull request Apr 21, 2020
fanquake added a commit that referenced this pull request Apr 22, 2020
8334ee3 scripts: add MACHO LAZY_BINDINGS test to test-security-check.py (fanquake)
7b99c74 scripts: add MACHO Canary check to security-check.py (fanquake)

Pull request description:

  7b99c74 uses `otool -Iv` to check for `___stack_chk_fail` in the macOS binaries. Similar to the [ELF check](https://github.com/bitcoin/bitcoin/blob/master/contrib/devtools/security-check.py#L105). Note that looking for a triple underscore prefixed function (as opposed to two for ELF) is correct for the macOS binaries. i.e:
  ```bash
  otool -Iv bitcoind | grep chk
  0x00000001006715b8   509 ___memcpy_chk
  0x00000001006715be   510 ___snprintf_chk
  0x00000001006715c4   511 ___sprintf_chk
  0x00000001006715ca   512 ___stack_chk_fail
  0x00000001006715d6   517 ___vsnprintf_chk
  0x0000000100787898   513 ___stack_chk_guard
  ```

  8334ee3 is a follow up to #18295 and adds test cases to `test-security-check.py` that for some reason I didn't add at the time. I'll sort out #18434 so that we can run these tests in the CI.

ACKs for top commit:
  practicalswift:
    ACK 8334ee3: Mitigations are important. Important things are worth asserting :)
  jonasschnelli:
    utACK 8334ee3.

Tree-SHA512: 1aa5ded34bbd187eddb112b27278deb328bfc21ac82316b20fab6ad894f223b239a76b53dab0ac1770d194c1760fcc40d4da91ec09959ba4fc8eadedb173936a
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Apr 23, 2020
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Feb 15, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants