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

Don't open /dev/urandom on startup #6077

Closed

Conversation

edwintorok
Copy link
Contributor

The host installer run networkd_db in a chroot without creating /dev/urandom there.
Removing the dependency on uuid (or on uuid generation) would be difficult, because it is closely tied to the Ref type which is used throughout xapi-types/xapi-idl/etc.

However we can use Mirage's crypto rng instead of opening /dev/urandom on startup, and this would use the getrandom syscall when available instead.

This works on Fedora 40, but I haven't tested yet whether it'd work in our much older CentOS 7 Dom0, hence opening as a draft.

Note that reverting the first commit is not enough, because there was also a followup commit that calls Random.State.make_self_init on startup, but that commit doesn't revert cleanly.

/dev/urandom may not be available.
mirage-crypto can use getrandom syscall if needed.

This is now fast enough that we don't need the Random based generator:
```
╭───────────────────────────────────────┬───────────────────────────┬───────────────────────────┬───────────────────────────╮
│name                                   │  major-allocated          │  minor-allocated          │  monotonic-clock          │
├───────────────────────────────────────┼───────────────────────────┼───────────────────────────┼───────────────────────────┤
│  uuidx creation/Uuidx.make            │             0.0080 mjw/run│            58.0004 mnw/run│            375.5079 ns/run│
│  uuidx creation/Uuidx.make_uuid_urnd  │             0.0000 mjw/run│            19.0059 mnw/run│           5510.3318 ns/run│
╰───────────────────────────────────────┴───────────────────────────┴───────────────────────────┴───────────────────────────╯
```

Using Random would be ~4x faster, but Fortuna is already >10x faster than /dev/urandom and avoids using system calls,
except for seeding:
```
│  uuidx creation/Uuidx.make            │             0.0000 mjw/run│            19.0001 mnw/run│             95.5587 ns/run│
```

There is also a newer version of mirage-crypto-rng that can avoid the Cstruct and give us the string more directly, which we'll soon upgrade to.

Signed-off-by: Edwin Török <[email protected]>
let make_uuid_fast () = with_non_csprng_state Uuidm.v4_gen
let fd = Unix.openfile dev [Unix.O_RDONLY] 0o640 in
let finally body_f clean_f =
try
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't take finally from a library or use Fun.protect?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a reformat.

@edwintorok
Copy link
Contributor Author

The code works, but I can't find any guarantees that mirage-crypto-rng is thread safe (this may not matter for the usage we already have in XAPI, where we generate a salt).
There are no warnings in its documentation about it not being thread-safe, but I looked at the implementation and it looks very unsafe. I've opened an issue here mirage/mirage-crypto#249.

I'll need to go with a different approach.

@edwintorok edwintorok closed this Oct 25, 2024
github-merge-queue bot pushed a commit that referenced this pull request Oct 29, 2024
… reduce number of syscalls (#6085)

This is an alternative to
#6077

2 recent optimizations have changed the Uuidx module to open
  /dev/urandom once on startup, instead of every time a value was
  requested.

However 'networkd_db' runs in the installer environment, inside a chroot
where /dev/urandom is not available.

Open /dev/urandom on first use instead.

Simplify the code and use a single implementation for both fast and
secure urandom generation:
* use a mutex to protect accesses to global urandom state
* use an input channel, rather than a Unix file descriptor, this allows
us to read many bytes in one go, and then generate multiple random
numbers without having to make syscalls that often

(syscalls are slow in this case because they require releasing the
runtime mutex, which gives another thread the opportunity to run for
50ms).

Fixes: a0176da ("CP-49135: open /dev/urandom just once")
Fixes: a2d9fbe ("IH-577 Implement v7 UUID generation")
Fixes: 6635a00 ("CP-49136: Introduce PRNG for generating non-secret
UUIDs")

This is slightly slower than before, but still fast enough:
```
│  uuidx creation/Uuidx.make            │             0.0004 mjw/run│            16.0001 mnw/run│            105.8801 ns/run│
│  uuidx creation/Uuidx.make_uuid_urnd  │             0.0004 mjw/run│            16.0001 mnw/run│            105.1474 ns/run│
```

Previously this used to take ~88ns, so in fact the difference is barely
noticable.

Also remove the feature flag: the previous change was feature flagged
too, but broke master anyway (I wouldn't have though anything *doesn't*
have /dev/urandom available, and didn't feature flag that part, because
in general it is not possible to feature flag startup code without
races).

`networkd_db` now doesn't try to open this anymore:
```
strace -e openat -e open networkd_db
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/librt.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/var/lib/xcp/networkd.db", O_RDONLY) = 3
+++ exited with 0 +++
```
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

Successfully merging this pull request may close these issues.

2 participants