Skip to contentSkip to conversation

random: collect cpu randomness

RFC PATCH In review Jörn Engel 9 participants 19 messages Feb 2, 2014 – Mar 23, 2014

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 random: collect cpu randomness

Collects entropy from random behaviour all modern cpus exhibit. The scheduler and slab allocator are instrumented for this purpose. How much randomness can be gathered is clearly hardware-dependent and hard to estimate. Therefore the entropy estimate is zero, but random bits still get mixed into the pools.

Performance overhead seems low. I did 20 kernel compiles with allnoconfig, everything precompiled and warm caches. Difference was in the noise and on average performance was actually better when collecting randomness.

To judge the benefits I ran this in kvm with instrumentation to print out the various input values. Entropy was estimated by booting ten times, collecting the output for each boot, doing a diff between any two and counting the diff hunks. Numbers like "46 .. 215" means the two most similar runs had 46 hunks, the two most dissimilar ones had 215.

Running kvm with -smp 4 gave the following results:
slab+sched slab sched

input[0]         46 .. 215      202 .. 342        0 ..   0
input[1]         76 .. 180        0 ..   4        0 ..   0
input[2]        147 .. 388        4 ..  44      286 .. 464
input[3]         56 .. 185        0 ..   2        9 ..  40
jiffies           8 ..  67       10 ..  28       19 ..  50
caller          114 .. 306       25 .. 178      219 .. 422
val              54 .. 254       15 .. 106      175 .. 321

&input 50 .. 246 0 .. 59 178 .. 387

First column collected entropy from both the slab allocator and the scheduler, second and third only collected from one source. I only used the first 512 values per cpu. Therefore the combined numbers can be lower than the individual ones - there was more entropy collected, it was just swamped by non-entropy.

Lots of entropy gets collected and about half of it stems from the uninitialized valued of input[] on the stack.

Rerunning only the first column with -smp 1 was more sobering:
slab+sched

input[0]          0 ..  13
input[1]          0 ..  13
input[2]          0 ..  14
input[3]          0 ..  11
jiffies           2 ..  22
caller            0 ..  19
val               0 ..  16
&input            0 ..   4

Every single input value contains entropy in some runs, but the only one that was an entropy guarantee was jiffies. In other words, a low-precision clock. This clearly shows how important it is to have a high-precision clock, which would gather significantly more entropy.

Measuring the randomness from random_get_entropy() with above approach failed because there was so much randomness. All numbers in all runs were different. Taking the delta between the numbers, again almost all numbers were different with at most 1 identical delta per 1000. Compared to a high-precision clock, no other input comes within two orders of magnitude.

An interesting aside is that the cpu actually functions as a high-precision clock. This partially explains why the -smp 4 number are so much better - any drift between the four cpu-clocks gets turned into randomness. The other explanation is that kvm is actually collecting randomness from the host system. I tried to minimize that effect by not touching my notebook while running these tests, but a hardware test box would clearly yield more meaningful results.

I invite others to also test whether the patch collects useful randomness or causes a measureable performance loss on any hardware or workload. Preferrably not using my methods, in order to avoid systematic errors.

---
 drivers/char/random.c  | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/random.h |  2 ++
 kernel/sched/core.c    |  1 +
 mm/slab.c              |  1 +
 4 files changed, 65 insertions(+)
drivers/char/random.c +61 -0
index 429b75bb60e8..693dea730a3e 100644
@@ -587,6 +587,30 @@ static void fast_mix(struct fast_pool *f, __u32 input[4])
 }
 
 /*
+ * An even faster variant of fast_mix, albeit with worse mixing.
+ */
+static void weak_mix(struct fast_pool *f, __u32 input[4])
+{
+	__u32 acc, carry = f->pool[3] >> 25;
+
+	acc = (f->pool[0] << 7) ^ input[0] ^ carry;
+	carry = f->pool[0] >> 25;
+	f->pool[0] = acc;
+
+	acc = (f->pool[1] << 7) ^ input[1] ^ carry;
+	carry = f->pool[1] >> 25;
+	f->pool[1] = acc;
+
+	acc = (f->pool[2] << 7) ^ input[2] ^ carry;
+	carry = f->pool[2] >> 25;
+	f->pool[2] = acc;
+
+	acc = (f->pool[3] << 7) ^ input[3] ^ carry;
+	//carry = f->pool[3] >> 25;
+	f->pool[3] = acc;
+}
+
+/*
  * Credit (or debit) the entropy store with n bits of entropy.
  * Use credit_entropy_bits_safe() if the value comes from userspace
  * or otherwise should be checked for extreme values.
@@ -833,6 +857,43 @@ void add_input_randomness(unsigned int type, unsigned int code,
 }
 EXPORT_SYMBOL_GPL(add_input_randomness);
 
+static DEFINE_PER_CPU(struct fast_pool, cpu_randomness);
+
+void __add_cpu_randomness(void *caller, void *val)
+{
+	struct entropy_store	*r;
+	struct fast_pool	*fast_pool = &__get_cpu_var(cpu_randomness);
+	unsigned long		now = jiffies;
+	__u32			input[4], cycles = random_get_entropy();
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuninitialized"
+	input[0] ^= cycles ^ jiffies;
+	input[1] ^= (unsigned long)caller;
+	input[2] ^= (unsigned long)val;
+	input[3] ^= (unsigned long)&input;
+#pragma GCC diagnostic pop
+
+	weak_mix(fast_pool, input);
+
+	if ((fast_pool->count++ & 1023) &&
+	    !time_after(now, fast_pool->last + HZ))
+		return;
+
+	fast_pool->last = now;
+	fast_pool->count = 1;
+
+	r = nonblocking_pool.initialized ? &input_pool : &nonblocking_pool;
+	__mix_pool_bytes(r, &fast_pool->pool, sizeof(fast_pool->pool), NULL);
+}
+
+void add_cpu_randomness(void *caller, void *val)
+{
+	preempt_disable();
+	__add_cpu_randomness(caller, val);
+	preempt_enable();
+}
+
 static DEFINE_PER_CPU(struct fast_pool, irq_randomness);
 
 void add_interrupt_randomness(int irq, int irq_flags)
include/linux/random.h +2 -0
index 4002b3df4c85..ce0ccdcd1d63 100644
@@ -12,6 +12,8 @@
 extern void add_device_randomness(const void *, unsigned int);
 extern void add_input_randomness(unsigned int type, unsigned int code,
 				 unsigned int value);
+extern void __add_cpu_randomness(void *caller, void *val);
+extern void add_cpu_randomness(void *caller, void *val);
 extern void add_interrupt_randomness(int irq, int irq_flags);
 
 extern void get_random_bytes(void *buf, int nbytes);
kernel/sched/core.c +1 -0
index a88f4a485c5e..7af6389f9b9e 100644
@@ -2511,6 +2511,7 @@ need_resched:
 	rq = cpu_rq(cpu);
 	rcu_note_context_switch(cpu);
 	prev = rq->curr;
+	__add_cpu_randomness(__builtin_return_address(1), prev);
 
 	schedule_debug(prev);
 
mm/slab.c +1 -0
index eb043bf05f4c..ea5a30d44ad1 100644
@@ -3587,6 +3587,7 @@ static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,
 	trace_kmalloc(caller, ret,
 		      size, cachep->size, flags);
 
+	add_cpu_randomness(__builtin_return_address(2), ret);
 	return ret;
 }
 
Signature · 1 line
1.8.5.2
#1 lore raw

Am Sonntag, 2. Februar 2014, 15:36:17 schrieb Jörn Engel:

Hi Jörn,

Quoted 5 lines

Collects entropy from random behaviour all modern cpus exhibit. The scheduler and slab allocator are instrumented for this purpose. How much randomness can be gathered is clearly hardware-dependent and hard to estimate. Therefore the entropy estimate is zero, but random bits still get mixed into the pools.

May I ask what the purpose of the patches is when no entropy is implied? I see that the pool is stirred more. But is that really a problem that needs addressing?

Please, do not get me wrong with the presented critisism here -- the approach in general looks interesting.

However, the following patches makes me wonder big time.

Quoted 18 lines

extern void get_random_bytes(void *buf, int nbytes);

kernel/sched/core.c +1 -0
index a88f4a485c5e..7af6389f9b9e 100644
@@ -2511,6 +2511,7 @@ need_resched:
 	rq = cpu_rq(cpu);
 	rcu_note_context_switch(cpu);
 	prev = rq->curr;
+	__add_cpu_randomness(__builtin_return_address(1), prev);
 
 	schedule_debug(prev);
 
mm/slab.c +0 -0
index eb043bf05f4c..ea5a30d44ad1 100644

mm/slab.c static __always_inline void *__do_kmalloc(size_t size, · line 3592

Quoted 6 lines
mm/slab.c +1 -0
@@ -3587,6 +3587,7 @@ static __always_inline void *__do_kmalloc(size_t size,
 gfp_t flags, trace_kmalloc(caller, ret,
 		      size, cachep->size, flags);
 
+	add_cpu_randomness(__builtin_return_address(2), ret);
 	return ret;
 }

First, the noise source you add is constantly triggered throughout the execution of the kernel. Entropy is very important, we (who are interested in crypto) know that. But how often is entropy needed? Other folks wonder about the speed of the kernel. And with these two patches, every kmalloc and every scheduling invocation now dives into the random.c code to do something. I would think this is a bit expensive, especially to stir the pool without increasing the entropy estimator. I think entropy collection should be performed when it is needed and not throughout the lifetime of the system.

Second, when I offered my initial patch which independently collects some entropy on the CPU execution timing, I got shot down with one concern raised by Ted, and that was about whether a user can influence the entropy collection process. When I am trying to measure CPU execution timing in the RNG, the concern was raised that the measured timing variations was due to CPU states that were influenced by users. Your patch here clearly hooks into code paths which are definitely affected by user actions. So, this patch therefore would be subject to the same concerns. I personally think that this is not so much an issue, yet it was raised previously.

It seems I have a bad timing, because just two days ago I released a new attempt on the CPU jitter RNG [1] with a new noise source, and I was just about to prepare a release email. With that attempt, both issues raised above are addressed, including a theoretical foundation of the noise source.

[1] http://www.chronox.de/

Ciao
Stephan

Signature · 1 line
| Cui bono? |
#2 lore raw
Quoted 11 lines · Stephan Mueller

Am Sonntag, 2. Februar 2014, 15:36:17 schrieb Jörn Engel:

Quoted 5 lines

Collects entropy from random behaviour all modern cpus exhibit. The scheduler and slab allocator are instrumented for this purpose. How much randomness can be gathered is clearly hardware-dependent and hard to estimate. Therefore the entropy estimate is zero, but random bits still get mixed into the pools.

May I ask what the purpose of the patches is when no entropy is implied? I see that the pool is stirred more. But is that really a problem that needs addressing?

For my part, I think the whole business of estimating entropy is bordering on the esoteric. If the hash on the output side is any good, you have a completely unpredictable prng once the entropy pool is unpredictable. Additional random bits are nice, but not all that useful. Blocking /dev/random based on entropy estimates is likewise not all that useful.

Key phrase is "once the entropy pool is unpredictable". So early in bootup it may make sense to estimate the entropy. But here the problem is that you cannot measure entropy, at least not within a single system and a reasonable amount of time. That leaves you with a heuristic that, like all heuristics, is wrong.

I personally care more about generating high-quality randomness as soon as possible and with low cost to the system. Feel free to disagree or set your priorities differently.

Quoted 26 lines

Please, do not get me wrong with the presented critisism here -- the approach in general looks interesting.

However, the following patches makes me wonder big time.

Quoted 21 lines

extern void get_random_bytes(void *buf, int nbytes);

kernel/sched/core.c +1 -0
index a88f4a485c5e..7af6389f9b9e 100644
@@ -2511,6 +2511,7 @@ need_resched:
 	rq = cpu_rq(cpu);
 	rcu_note_context_switch(cpu);
 	prev = rq->curr;
+	__add_cpu_randomness(__builtin_return_address(1), prev);
 
 	schedule_debug(prev);
 
mm/slab.c +0 -0
index eb043bf05f4c..ea5a30d44ad1 100644
@@ -3587,6 +3587,7 @@ static __always_inline void *__do_kmalloc(size_t size,
 gfp_t flags, trace_kmalloc(caller, ret,
 		      size, cachep->size, flags);
 
Quoted 12 lines

mm/slab.c static __always_inline void *__do_kmalloc(size_t size, gfp_t flags, · line 3592

Quoted 3 lines
mm/slab.c +1 -0
+	add_cpu_randomness(__builtin_return_address(2), ret);
 	return ret;
 }

First, the noise source you add is constantly triggered throughout the execution of the kernel. Entropy is very important, we (who are interested in crypto) know that. But how often is entropy needed? Other folks wonder about the speed of the kernel. And with these two patches, every kmalloc and every scheduling invocation now dives into the random.c code to do something. I would think this is a bit expensive, especially to stir the pool without increasing the entropy estimator. I think entropy collection should be performed when it is needed and not throughout the lifetime of the system.

Please measure how expensive it really is. My measurement gave me a "doesn't matter" result, surprising as it may seem.

If the cost actually matters, we can either disable or rate-limit the randomness collection at some point after boot. But that would bring us back into the estimation business.

Quoted 9 lines

Second, when I offered my initial patch which independently collects some entropy on the CPU execution timing, I got shot down with one concern raised by Ted, and that was about whether a user can influence the entropy collection process. When I am trying to measure CPU execution timing in the RNG, the concern was raised that the measured timing variations was due to CPU states that were influenced by users. Your patch here clearly hooks into code paths which are definitely affected by user actions. So, this patch therefore would be subject to the same concerns. I personally think that this is not so much an issue, yet it was raised previously.

The nice thing about the random pool is that mixing any amount of deterministic data into it does not diminish the randomness already in it. Given that attribute, I don't understand the concern.

Quoted 6 lines

It seems I have a bad timing, because just two days ago I released a new attempt on the CPU jitter RNG [1] with a new noise source, and I was just about to prepare a release email. With that attempt, both issues raised above are addressed, including a theoretical foundation of the noise source.

[1] http://www.chronox.de/

I am not married to my patch. If the approach makes sense, let's merge it. If the approach does not make sense or there is a better alternative, drop it on the floor.

The problem I see with your approach is this:
"The only prerequisite is the availability of a high-resolution timer that is available in modern CPUs."

Given a modern CPU with a high-resolution timer, you will almost certainly collect enough randomness for good random numbers. Problem solved and additional improvements are useless.

But on embedded systems with less modern CPUs, few interrupt sources, no user interface, etc. you may have trouble collecting enough randomness or doing it soon enough. That is the problem worth fixing. It is also a hard problem to fix and I am not entirely convinced I found a good approach.

Jörn

--
It's just what we asked for, but not what we want! -- anonymous

#3 lore raw
Quoted 8 lines · Jörn Engel

For my part, I think the whole business of estimating entropy is bordering on the esoteric. If the hash on the output side is any good, you have a completely unpredictable prng once the entropy pool is unpredictable. Additional random bits are nice, but not all that useful. Blocking /dev/random based on entropy estimates is likewise not all that useful.

Quoted 6 lines

Key phrase is "once the entropy pool is unpredictable". So early in bootup it may make sense to estimate the entropy. But here the problem is that you cannot measure entropy, at least not within a single system and a reasonable amount of time. That leaves you with a heuristic that, like all heuristics, is wrong.

The entropy bound needs to be a conservative lower bound. Its main use is to provide backpressure (should we spend more CPU time producing entropy) although the forward pressure on /dev/random is potentially useful for high security applications.

This does NOT mean that zero-credit entropy generation is useless, far from it. It just means that we are doing it on an "it can't hurt" basis, rather than "I know for sure that this is valuable."

-hpa

maintainer
#5 lore raw
In reply to Stephan Mueller
Quoted 4 lines · Stephan Mueller

Second, when I offered my initial patch which independently collects some entropy on the CPU execution timing, I got shot down with one concern raised by Ted, and that was about whether a user can influence the entropy collection process.

Um, that wasn't my concern. After all, when we sample keyboard timing while trying to generate a GPG key, of course the user can and does influence the entropy collection process.

The question is whether an attacker who has deep knowledge of the how the CPU works internally, perhaps made worse with quantization effects (i.e., it doesn't matter if analog-generated settling time is measured in microseconds if the output is being clocked out in milliseconds), such that it is predictable.

I really like Jörn's tests doing repeated boot testing and observing on a SMP system, the slab allocation pattern is quite deterministic. So even though the numbers might *look* random, an attacker with deep knowledge of how the kernel was compiled and what memory allocations get done during the boot sequence would be able to quite successfuly measure it.

I'm guessing that indeed, on a 4-CPU KVM system, what you're measuring is the when the host OS happens to be scheduling the KVM threads, with some variability caused by external networking interrupts, etc. It would definitely be a good idea to retry that experiment on a real 4-CPU system to see what sort of results you might get. It might very well be that the attacker who knows the relative ordering of the slab/thread activations but for which it's not entirely clear whether one cpu will be ahead of another, that there is *some* entropy, but perhaps only a handful bits. It's the fact that we can't be sure how much uncertainty there might be with an attacker with very deep knowledge the CPU which is why Jörn's conservatism of not crediting the entropy counter is quite understandable.

Of course, this doesn't help someone who is trying to speed up the time it takes GPG to generate a new key pair. But in terms of improving /dev/urandom as it is used by many crypto applications, it certainly can't hurt.

The real question is how much overhead does it add, and is it worth it. Jörn, I take it that was the reason for creating an even faster, but weaker mixing function? Was the existing "fast mix" causing a measurable overhead, or was this your just being really paranoid about not adding anything to the various kernel fastpaths?

- Ted

#6 lore raw
Quoted 6 lines · Theodore Ts'o

The real question is how much overhead does it add, and is it worth it. Jörn, I take it that was the reason for creating an even faster, but weaker mixing function? Was the existing "fast mix" causing a measurable overhead, or was this your just being really paranoid about not adding anything to the various kernel fastpaths?

It was paranoia. And I am still somewhat paranoid and don't trust my benchmark results yet. Maybe on an 1024-CPU Altix with a 100k-thread workload the overhead is too much. Just because I couldn't measure a difference on my wimpy notebook does not mean much.

Jörn

--
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision.
-- Bertrand Russell

#7 lore raw
In reply to Theodore Ts'o

Am Sonntag, 2. Februar 2014, 20:39:22 schrieb Theodore Ts'o:

Hi Theodore,

Quoted 9 lines · Stephan Mueller
Quoted 4 lines

Second, when I offered my initial patch which independently collects some entropy on the CPU execution timing, I got shot down with one concern raised by Ted, and that was about whether a user can influence the entropy collection process.

Um, that wasn't my concern. After all, when we sample keyboard timing while trying to generate a GPG key, of course the user can and does influence the entropy collection process.

Thank you for clarifying and sorry that I misunderstood you.

Quoted 8 lines

I really like Jörn's tests doing repeated boot testing and observing on a SMP system, the slab allocation pattern is quite deterministic. So even though the numbers might *look* random, an attacker with deep knowledge of how the kernel was compiled and what memory allocations get done during the boot sequence would be able to quite successfuly measure it.

I'm guessing that indeed, on a 4-CPU KVM system, what you're measuring

Please let me point out that I am not testing on KVM, but on Linux running natively on the hardware. All major CPUs were tested. Even tests are executed on bare metal, i.e. without any OS and interrupts disabled. But I will explain that in a separate email and do not want to hijack this thread.

Ciao
Stephan

#8 lore raw
In reply to Theodore Ts'o

Theodore Ts'o dixit:

Quoted 4 lines

I really like Jvrn's tests doing repeated boot testing and observing on a SMP system, the slab allocation pattern is quite deterministic. So even though the numbers might *look* random, an attacker with deep knowledge of how the kernel was compiled and what memory allocations

For this reason, we pre-initialise (one of) the RNG during kernel compile time, using host randomness, in MirBSD. We’re also considering pushing some additional seed using the bootloader, that can be mixed in very very early. As an added benefit, everything in the kernel can call arc4random(), arc4random_buf() and arc4random_uniform() without worrying about initialisation, at all times. (This is mostly for the places where it replaced random(), or which explicitly waited for the regular RNG initialisation to be done, or which reseeded after that.)

In GNU/Linux, this could work like, GRUB will offer the contents of /boot/grub/randseed.bin to the Linux kernel, which will add it into the pool using the normal mechanisms, and (very?) early userspace will then recreate that file (to prevent seed reuse, just like the normal /var/db/host.random or /var/lib/urandom/random-seed processing is done). Downside: write access to the boot medium needed. (No GRUB modification needed, this could be passed as “faux kernel module”.)
Upside: this is way earlier than anything user space can do, and e.g. early enough to affect things like kernel memory management.

bye,
//mirabilos

Signature · 4 lines
<Natureshadow> Ach, mach doch was du willst, du hast doch eh immer Recht!
<mirabilos> jupp ~/.etc/sig………
<Natureshadow> unfaßbar…
<Natureshadow> Mit Eszett sogar, unfaßbar!
#4 lore raw
In reply to Jörn Engel

Am Sonntag, 2. Februar 2014, 20:24:21 schrieb Jörn Engel:

Hi Jörn,

Quoted 16 lines · Stephan Mueller
Quoted 11 lines

Am Sonntag, 2. Februar 2014, 15:36:17 schrieb Jörn Engel:

Quoted 9 lines

Collects entropy from random behaviour all modern cpus exhibit. The
scheduler and slab allocator are instrumented for this purpose. How
much randomness can be gathered is clearly hardware-dependent and hard
to estimate. Therefore the entropy estimate is zero, but random bits
still get mixed into the pools.

Quoted 3 lines

May I ask what the purpose of the patches is when no entropy is implied? I see that the pool is stirred more. But is that really a problem that needs addressing?

Quoted 6 lines

For my part, I think the whole business of estimating entropy is bordering on the esoteric. If the hash on the output side is any good, you have a completely unpredictable prng once the entropy pool is unpredictable. Additional random bits are nice, but not all that useful. Blocking /dev/random based on entropy estimates is likewise not all that useful.

I really like that statement, because for the most part I concur :-)

However, there are a number of cryptographers out there, which insist on such entropy assessments and even the blocking behavior. For example, I work with cryptographers from the German BSI. We created a quantitative assessment of /dev/random for them (see [1]). During the discussion, I learned that the key reason they like /dev/random and dislike /dev/urandom is the fact that /dev/random ensures that any output of data is always backed by hardware entropy. In order to ensure that you always have hardware entropy that backs your output, you somehow must quantify that hardware entropy. Thus, a dropping of the entropy estimation would be catastrophic for them.

When you look at NIST and the base discussions in SP800-90A, you see that for deterministic RNGs, NIST is not that strict as BSI. Yet, they require a DRNG to be reseeded with entropy after a (large) number of generated bits. For that reseeding process, some entropy estimation is needed. But when looking at SP800-90B, things get hairy again where some strict entropy estimations are needed.

Even if you subscribe to the notion that an RNG only needs some X bits of entropy for starters and then can spin indefinitely on this entropy, there is still a need on estimating entropy, at least at the beginning.

Quoted 3 lines

Key phrase is "once the entropy pool is unpredictable". So early in bootup it may make sense to estimate the entropy. But here the

I am fully in agreement here.

Quoted 3 lines

problem is that you cannot measure entropy, at least not within a single system and a reasonable amount of time. That leaves you with a heuristic that, like all heuristics, is wrong.

No argument here :-)

(side note: The interesting thing is that the /dev/random heuristic on entropy seems to underestimate the entropy present in events where the heuristic assumes low entropy, but way overestimates entropy where the heuristic entropy assumes high entropy)

Quoted 4 lines

I personally care more about generating high-quality randomness as soon as possible and with low cost to the system. Feel free to disagree or set your priorities differently.

Fully in agreement

[..]

Quoted 10 lines
Quoted 10 lines

First, the noise source you add is constantly triggered throughout the
execution of the kernel. Entropy is very important, we (who are interested in crypto) know that. But how often is entropy needed? Other folks wonder about the speed of the kernel. And with these two patches, every kmalloc and every scheduling invocation now dives into the random.c code to do something. I would think this is a bit expensive, especially to stir the pool without increasing the entropy estimator. I think entropy collection should be performed when it is needed and not throughout the lifetime of the system.

Quoted 2 lines

Please measure how expensive it really is. My measurement gave me a "doesn't matter" result, surprising as it may seem.

That sounds really good.

Quoted 13 lines

If the cost actually matters, we can either disable or rate-limit the randomness collection at some point after boot. But that would bring us back into the estimation business.

Quoted 7 lines

It seems I have a bad timing, because just two days ago I released a new attempt on the CPU jitter RNG [1] with a new noise source, and I was just about to prepare a release email. With that attempt, both issues raised above are addressed, including a theoretical foundation of the noise source.

[1] http://www.chronox.de/

Quoted 7 lines

I am not married to my patch. If the approach makes sense, let's merge it. If the approach does not make sense or there is a better alternative, drop it on the floor.

The problem I see with your approach is this:
"The only prerequisite is the availability of a high-resolution timer that is available in modern CPUs."

Right, and with the absence of a high-resolution counter, my RNG breaks down. Though, I have more goals than to just run my RNG inside the Linux kernel and thus the reliance on only the timer.

However, during my testing on also embedded systems, a significant number have them -- at least a counter that is integrated with the clock_source framework.

Allow me to explain my RNG and the new developments in a separate email to not hijack your thread here.

I think both have merits, considering that based on your statement, the integration into schedule/kmalloc code paths is not that expensive.

Quoted 10 lines

Given a modern CPU with a high-resolution timer, you will almost certainly collect enough randomness for good random numbers. Problem solved and additional improvements are useless.

But on embedded systems with less modern CPUs, few interrupt sources, no user interface, etc. you may have trouble collecting enough randomness or doing it soon enough. That is the problem worth fixing. It is also a hard problem to fix and I am not entirely convinced I found a good approach.

I do not think that there can be a right approach given the variety of systems Linux can run on. Though, the current random.c definitely is challenged in embedded systems, but also on "normal" headless systems with SSDs. Thus, any proposal of using new entropy sources is good.

[1]
https://www.bsi.bund.de/DE/Publikationen/Studien/LinuxRNG/index_htm.html

Ciao
Stephan

#9 lore raw
In reply to Jörn Engel
Quoted 7 lines · Jörn Engel

Measuring the randomness from random_get_entropy() with above approach failed because there was so much randomness. All numbers in all runs were different. Taking the delta between the numbers, again almost all numbers were different with at most 1 identical delta per 1000. Compared to a high-precision clock, no other input comes within two orders of magnitude.

I think this is a key result from my tests. The best source is a timer (counter) that is both
a) high-resolution and
b) asynchronous to the measurement event.

If the measurement event is an interrupt and the CPU has a cycle-counter, you are set. On interesting systems lacking a cycle-counter, we still have a high-resolution counter or sorts that is the CPU itself.

Instruction pointer and stack pointer for both kernel and userland are one way to read out the "counter". Main problem here are tight loops where your "counter" is not high-resolution at all. But something within the CPU is constantly changing. And that something tends to be contained in the registers.

How about taking the saved registers from the interrupted CPU, xor'ing them all and calling the result random_get_entropy() on systems lacking a good cycles-counter?

Jörn

--
I can say that I spend most of my time fixing bugs even if I have lots of new features to implement in mind, but I give bugs more priority. -- Andrea Arcangeli, 2000

maintainer
#10 lore raw
Quoted 11 lines · Jörn Engel

If the measurement event is an interrupt and the CPU has a cycle-counter, you are set. On interesting systems lacking a cycle-counter, we still have a high-resolution counter or sorts that is the CPU itself.

Instruction pointer and stack pointer for both kernel and userland are one way to read out the "counter". Main problem here are tight loops where your "counter" is not high-resolution at all. But something within the CPU is constantly changing. And that something tends to be contained in the registers.

Quoted 3 lines

How about taking the saved registers from the interrupted CPU, xor'ing them all and calling the result random_get_entropy() on systems lacking a good cycles-counter?

So we could take the struct pt_regs which we get from get_irq_regs(), XOR them together and use them to feed into input[2] amd input[3] in add_interrupt_randomness(). Or some other way of distributing the values of all of the irq registers into the __u32 input[4] array.

That would probably be a good and useful thing to do. Was that basically what you were suggesting?

- Ted

#11 lore raw
Quoted 8 lines · Theodore Ts'o

So we could take the struct pt_regs which we get from get_irq_regs(), XOR them together and use them to feed into input[2] amd input[3] in add_interrupt_randomness(). Or some other way of distributing the values of all of the irq registers into the __u32 input[4] array.

That would probably be a good and useful thing to do. Was that basically what you were suggesting?

Yes.

Jörn

--
When you copy some code, you are supposed to read it. If nothing else, there's a chance to spot and fix an obvious bug instead of sharing it... -- Al Viro

#13 lore raw
In reply to Theodore Ts'o
Quoted 8 lines · Theodore Ts'o
Quoted 3 lines

How about taking the saved registers from the interrupted CPU, xor'ing them all and calling the result random_get_entropy() on systems lacking a good cycles-counter?

So we could take the struct pt_regs which we get from get_irq_regs(), XOR them together and use them to feed into input[2] amd input[3] in add_interrupt_randomness(). Or some other way of distributing the values of all of the irq registers into the __u32 input[4] array.

Can we be sure we don't leak information this way? Just being
paranoid...

Maciej

maintainer
#14 lore raw
Quoted 3 lines · Maciej W. Rozycki

Can we be sure we don't leak information this way? Just being
paranoid...

The register information will be mixed pretty thoroughly by the time it gets to the entropy pool, and then we don't ever expose the entropy pool to userspace. So no, I don't think we need to worry about leaking information.

- Ted

#15 lore raw
In reply to Jörn Engel

Hi Jörn,

Quoted 5 lines · Jörn Engel

Collects entropy from random behaviour all modern cpus exhibit. The scheduler and slab allocator are instrumented for this purpose. How much randomness can be gathered is clearly hardware-dependent and hard to estimate. Therefore the entropy estimate is zero, but random bits still get mixed into the pools.

Have you seen this work from PaX Team?

http://grsecurity.net/pipermail/grsecurity/2012-July/001093.html

See http://grsecurity.net/test/grsecurity-3.0-3.13.1-201402052349.patch
and search for PAX_LATENT_ENTROPY.

-Kees

Signature · 1 line
Kees Cook                                            @outflux.net
#16 lore raw
Quoted 9 lines · Kees Cook

Hi Jörn,

Quoted 5 lines · Jörn Engel

Collects entropy from random behaviour all modern cpus exhibit. The scheduler and slab allocator are instrumented for this purpose. How much randomness can be gathered is clearly hardware-dependent and hard to estimate. Therefore the entropy estimate is zero, but random bits still get mixed into the pools.

Quoted 6 lines

Have you seen this work from PaX Team?

http://grsecurity.net/pipermail/grsecurity/2012-July/001093.html

See http://grsecurity.net/test/grsecurity-3.0-3.13.1-201402052349.patch
and search for PAX_LATENT_ENTROPY.

The hardware rng world just got easier with the "hashlet".

https://plus.google.com/u/0/107942175615993706558/posts/4iq6W524SxL

Kernel driver wanted...

Quoted 4 lines

-Kees

--
Kees Cook @outflux.net

Signature · 3 lines
Dave Täht

Fixing bufferbloat with cerowrt: http://www.teklibre.com/cerowrt/subscribe.html
#17 lore raw
In reply to Kees Cook
Quoted 10 lines · Kees Cook
Quoted 5 lines · Jörn Engel

Collects entropy from random behaviour all modern cpus exhibit. The scheduler and slab allocator are instrumented for this purpose. How much randomness can be gathered is clearly hardware-dependent and hard to estimate. Therefore the entropy estimate is zero, but random bits still get mixed into the pools.

Have you seen this work from PaX Team?

http://grsecurity.net/pipermail/grsecurity/2012-July/001093.html

Interesting.

Quoted 2 lines

See http://grsecurity.net/test/grsecurity-3.0-3.13.1-201402052349.patch
and search for PAX_LATENT_ENTROPY.

Server gives me an error. Archive.org doesn't have a copy either, thanks to robots.txt.

Can you send me a copy via mail?

Jörn

--
Functionality is an asset, but code is a liability. --Ted Dziuba

#18 lore raw
In reply to Jörn Engel

Il 02/02/2014 21:36, Jörn Engel ha scritto:

drivers/char/random.c void add_input_randomness(unsigned int type, unsigned int code, · line 875

Quoted 7 lines
drivers/char/random.c +7 -0
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuninitialized"
+	input[0] ^= cycles ^ jiffies;
+	input[1] ^= (unsigned long)caller;
+	input[2] ^= (unsigned long)val;
+	input[3] ^= (unsigned long)&input;
+#pragma GCC diagnostic pop

Your tests demonstrate that this works, and presumably you have checked the assembly too. Still, this is invoking undefined behavior and the compiler could justifiably change those "^=" to "=".

An "asm" would be a safer way to convince the compiler that input[] is now initialized:

    asm volatile ("" :
        "=m" (input[0]), "=m" (input[1]),
        "=m" (input[2]), "=m" (input[3]));

and *really* XOR the values into the contents of the stack.

Of course the compiler could still have a "feature" where it pre-initializes the whole stack frame with some kind of canary, but that would be a problem even with your version of the code.

Paolo

#12 lore raw

PATCH 1/1 random: mix all saved registers into entropy pool

In reply to Jörn Engel
Quoted 11 lines · Jörn Engel
Quoted 8 lines · Theodore Ts'o

So we could take the struct pt_regs which we get from get_irq_regs(), XOR them together and use them to feed into input[2] amd input[3] in add_interrupt_randomness(). Or some other way of distributing the values of all of the irq registers into the __u32 input[4] array.

That would probably be a good and useful thing to do. Was that basically what you were suggesting?

Yes.

And here is a patch to do just that. I am extremely unhappy with it because it plain Does Not Help(tm). At least it does not when using kvm without VGA output. Booting kvm 1000 times gave me the exact same values for every single boot. Quite sobering.

This patch didn't collect a single bit of entropy!

Surprisingly I find this failure quite valuable. I managed to prove the futility of my method - at least on automated virtual machines without network interactions. Afaiu none of the other entropy sources have proven to be of any value under these circumstances either. It is entirely possible that zero bits of entropy is the final verdict for such virtual machines. Not sure about everyone else, but that is quite a surprise to me.

Next step will be to test this patch on some entropy-challenged hardware.

Jörn

--
The object-oriented version of 'Spaghetti code' is, of course, 'Lasagna code'. (Too many layers).
-- Roberto Waltman.

The single biggest entropy source is a high-resolution timer running asynchronous to the triggering event. That leaves systems without a useful get_cycles() implementation with significantly less entropy. Significant means orders of magnitude, not a few percent.

An alternate high-resolution timer is the register content at the time of an interrupt. While not monotonic, it is guaranteed to change every few clock cycles and very unlikely to repeat the same pattern. Not useful for interpretation as time, but we only care about some bits of the "clock" to flip in an unpredictable fashion.

---
 drivers/char/random.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 81 insertions(+), 4 deletions(-)
drivers/char/random.c +81 -4
index 693dea730a3e..4a0633973aa7 100644
@@ -257,6 +257,7 @@
 #include <linux/kmemcheck.h>
 #include <linux/workqueue.h>
 #include <linux/irq.h>
+#include <linux/crc32.h>
 
 #include <asm/processor.h>
 #include <asm/uaccess.h>
@@ -553,6 +554,8 @@ static void mix_pool_bytes(struct entropy_store *r, const void *in,
 
 struct fast_pool {
 	__u32		pool[4];
+	u32		last_shift;
+	u32		regs_count;
 	unsigned long	last;
 	unsigned short	count;
 	unsigned char	rotate;
@@ -896,6 +899,79 @@ void add_cpu_randomness(void *caller, void *val)
 
 static DEFINE_PER_CPU(struct fast_pool, irq_randomness);
 
+/*
+ * Ratelimit to a steady state of about once per jiffy.  A naïve approach
+ * would be to return 1 every time jiffies changes.  But we want to avoid
+ * being closely coupled to the timer interrupt.  So instead we increment
+ * a counter on every call and shift it right every time jiffies changes.
+ * If the counter is a power of two, return false;
+ *
+ * Effect is that some time after a jiffies change and cutting the counter
+ * in half we reach another power of two and return false.  But the
+ * likelihood of this happening is about the same at any time within a
+ * jiffies interval.
+ */
+static inline int ratelimited(struct fast_pool *p)
+{
+	int ret = !(p->regs_count == 0 || is_power_of_2(p->regs_count));
+
+	p->regs_count++;
+	if (p->last_shift != (u32)jiffies) {
+		p->regs_count >>= min(31u, (u32)jiffies - p->last_shift);
+		p->last_shift = (u32)jiffies;
+	}
+	return ret;
+}
+
+#define BOOT_IRQS 1024
+
+#if 1 /* XXX: create config option, as it is a bit spammy */
+/*
+ * We have a few conflicting requirements.  In order to judge the quality
+ * of randomness we want to print out inputs on the console.  Clearly we
+ * also want to keep the inputs secret.  Tradeoff is to print a hash of
+ * the inputs a couple of times during boot.
+ */
+static void trace_boot_regs(struct pt_regs *regs, int boot_count)
+{
+	static u32 log[BOOT_IRQS];
+	int i;
+	log[BOOT_IRQS - boot_count] = crc32(0, regs, sizeof(*regs));
+	if (boot_count == 1) {
+		for (i = 0; i < BOOT_IRQS; i++)
+			pr_info("irq regs(%d): %x\n", i, log[i]);
+	}
+}
+#endif
+
+/*
+ * The single biggest entropy source is a high-resolution timer running
+ * asynchronous to the triggering event.  That leaves systems without a
+ * useful get_cycles() implementation with significantly less entropy.
+ * Significant means orders of magnitude, not a few percent.
+ *
+ * An alternate high-resolution timer is the register content at the time
+ * of an interrupt.  While not monotonic, it is guaranteed to change every
+ * few clock cycles and very unlikely to repeat the same pattern.  Not
+ * useful for interpretation as time, but we only care about some bits
+ * of the "clock" to flip in an unpredictable fashion.
+ */
+static void mix_regs(struct pt_regs *regs, struct fast_pool *fast_pool)
+{
+	/* Two variables avoid decrementing by two without using atomics */
+	static int boot_count = BOOT_IRQS;
+	int in_boot = boot_count;
+
+	if (in_boot) {
+		trace_boot_regs(regs, boot_count);
+		boot_count = in_boot - 1;
+	} else if (ratelimited(fast_pool))
+		return;
+
+	mix_pool_bytes(&input_pool, regs, sizeof(*regs), NULL);
+	mix_pool_bytes(&nonblocking_pool, regs, sizeof(*regs), NULL);
+}
+
 void add_interrupt_randomness(int irq, int irq_flags)
 {
 	struct entropy_store	*r;
@@ -906,13 +982,14 @@ void add_interrupt_randomness(int irq, int irq_flags)
 	__u32			input[4], c_high, j_high;
 	__u64			ip;
 
+	mix_regs(regs, fast_pool);
 	c_high = (sizeof(cycles) > 4) ? cycles >> 32 : 0;
 	j_high = (sizeof(now) > 4) ? now >> 32 : 0;
-	input[0] = cycles ^ j_high ^ irq;
-	input[1] = now ^ c_high;
+	input[0] ^= cycles ^ j_high ^ irq;
+	input[1] ^= now ^ c_high;
 	ip = regs ? instruction_pointer(regs) : _RET_IP_;
-	input[2] = ip;
-	input[3] = ip >> 32;
+	input[2] ^= ip;
+	input[3] ^= ip >> 32;
 
 	fast_mix(fast_pool, input);
 
Signature · 1 line
1.8.5.3