Skip to content

Commit

Permalink
Merge branch 'net_get_random_once'
Browse files Browse the repository at this point in the history
Hannes Frederic Sowa says:

====================
This series implements support for delaying the initialization of secret
keys, e.g. used for hashing, for as long as possible. This functionality
is implemented by a new macro, net_get_random_bytes.

I already used it to protect the socket hashes, the syncookie secret
(most important) and the tcp_fastopen secrets.

Changelog:
v2) Use static_keys in net_get_random_once to have as minimal impact to
    the fast-path as possible.
v3) added patch "static_key: WARN on usage before jump_label_init was called":
    Patch "x86/jump_label: expect default_nop if static_key gets enabled
    on boot-up" relaxes the checks for using static_key primitives before
    jump_label_init. So tighten them first.
v4) Update changelog on the patch "static_key: WARN on usage before
    jump_label_init was called"

Included patches:
 ipv4: split inet_ehashfn to hash functions per compilation unit
 ipv6: split inet6_ehashfn to hash functions per compilation unit
 static_key: WARN on usage before jump_label_init was called
 x86/jump_label: expect default_nop if static_key gets enabled on boot-up
 net: introduce new macro net_get_random_once
 inet: split syncookie keys for ipv4 and ipv6 and initialize with net_get_random_once
 inet: convert inet_ehash_secret and ipv6_hash_secret to net_get_random_once
 tcp: switch tcp_fastopen key generation to net_get_random_once
 net: switch net_secret key generation to net_get_random_once
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Oct 19, 2013
2 parents 53481da + e34c9a6 commit 7dcade3
Show file tree
Hide file tree
Showing 23 changed files with 278 additions and 131 deletions.
25 changes: 18 additions & 7 deletions arch/x86/kernel/jump_label.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,27 @@ static void __jump_label_transform(struct jump_entry *entry,
int init)
{
union jump_code_union code;
const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP };
const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5];

if (type == JUMP_LABEL_ENABLE) {
/*
* We are enabling this jump label. If it is not a nop
* then something must have gone wrong.
*/
if (unlikely(memcmp((void *)entry->code, ideal_nop, 5) != 0))
bug_at((void *)entry->code, __LINE__);
if (init) {
/*
* Jump label is enabled for the first time.
* So we expect a default_nop...
*/
if (unlikely(memcmp((void *)entry->code, default_nop, 5)
!= 0))
bug_at((void *)entry->code, __LINE__);
} else {
/*
* ...otherwise expect an ideal_nop. Otherwise
* something went horribly wrong.
*/
if (unlikely(memcmp((void *)entry->code, ideal_nop, 5)
!= 0))
bug_at((void *)entry->code, __LINE__);
}

code.jump = 0xe9;
code.offset = entry->target -
Expand All @@ -63,7 +75,6 @@ static void __jump_label_transform(struct jump_entry *entry,
* are converting the default nop to the ideal nop.
*/
if (init) {
const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP };
if (unlikely(memcmp((void *)entry->code, default_nop, 5) != 0))
bug_at((void *)entry->code, __LINE__);
} else {
Expand Down
10 changes: 10 additions & 0 deletions include/linux/jump_label.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@

#include <linux/types.h>
#include <linux/compiler.h>
#include <linux/bug.h>

extern bool static_key_initialized;

#define STATIC_KEY_CHECK_USE() WARN(!static_key_initialized, \
"%s used before call to jump_label_init", \
__func__)

#if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL)

Expand Down Expand Up @@ -128,6 +135,7 @@ struct static_key {

static __always_inline void jump_label_init(void)
{
static_key_initialized = true;
}

static __always_inline bool static_key_false(struct static_key *key)
Expand All @@ -146,11 +154,13 @@ static __always_inline bool static_key_true(struct static_key *key)

static inline void static_key_slow_inc(struct static_key *key)
{
STATIC_KEY_CHECK_USE();
atomic_inc(&key->enabled);
}

static inline void static_key_slow_dec(struct static_key *key)
{
STATIC_KEY_CHECK_USE();
atomic_dec(&key->enabled);
}

Expand Down
2 changes: 2 additions & 0 deletions include/linux/jump_label_ratelimit.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ struct static_key_deferred {
};
static inline void static_key_slow_dec_deferred(struct static_key_deferred *key)
{
STATIC_KEY_CHECK_USE();
static_key_slow_dec(&key->key);
}
static inline void
jump_label_rate_limit(struct static_key_deferred *key,
unsigned long rl)
{
STATIC_KEY_CHECK_USE();
}
#endif /* HAVE_JUMP_LABEL */
#endif /* _LINUX_JUMP_LABEL_RATELIMIT_H */
25 changes: 25 additions & 0 deletions include/linux/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,31 @@ do { \
#define net_random() prandom_u32()
#define net_srandom(seed) prandom_seed((__force u32)(seed))

bool __net_get_random_once(void *buf, int nbytes, bool *done,
struct static_key *done_key);

#ifdef HAVE_JUMP_LABEL
#define ___NET_RANDOM_STATIC_KEY_INIT ((struct static_key) \
{ .enabled = ATOMIC_INIT(0), .entries = (void *)1 })
#else /* !HAVE_JUMP_LABEL */
#define ___NET_RANDOM_STATIC_KEY_INIT STATIC_KEY_INIT_FALSE
#endif /* HAVE_JUMP_LABEL */

/* BE CAREFUL: this function is not interrupt safe */
#define net_get_random_once(buf, nbytes) \
({ \
bool ___ret = false; \
static bool ___done = false; \
static struct static_key ___done_key = \
___NET_RANDOM_STATIC_KEY_INIT; \
if (!static_key_true(&___done_key)) \
___ret = __net_get_random_once(buf, \
nbytes, \
&___done, \
&___done_key); \
___ret; \
})

int kernel_sendmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec,
size_t num, size_t len);
int kernel_recvmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec,
Expand Down
28 changes: 7 additions & 21 deletions include/net/inet6_hashtables.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,14 @@

struct inet_hashinfo;

static inline unsigned int inet6_ehashfn(struct net *net,
const struct in6_addr *laddr, const u16 lport,
const struct in6_addr *faddr, const __be16 fport)
static inline unsigned int __inet6_ehashfn(const u32 lhash,
const u16 lport,
const u32 fhash,
const __be16 fport,
const u32 initval)
{
u32 ports = (((u32)lport) << 16) | (__force u32)fport;

return jhash_3words((__force u32)laddr->s6_addr32[3],
ipv6_addr_jhash(faddr),
ports,
inet_ehash_secret + net_hash_mix(net));
}

static inline int inet6_sk_ehashfn(const struct sock *sk)
{
const struct inet_sock *inet = inet_sk(sk);
const struct in6_addr *laddr = &sk->sk_v6_rcv_saddr;
const struct in6_addr *faddr = &sk->sk_v6_daddr;
const __u16 lport = inet->inet_num;
const __be16 fport = inet->inet_dport;
struct net *net = sock_net(sk);

return inet6_ehashfn(net, laddr, lport, faddr, fport);
const u32 ports = (((u32)lport) << 16) | (__force u32)fport;
return jhash_3words(lhash, fhash, ports, initval);
}

int __inet6_hash(struct sock *sk, struct inet_timewait_sock *twp);
Expand Down
26 changes: 6 additions & 20 deletions include/net/inet_sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,30 +204,16 @@ static inline void inet_sk_copy_descendant(struct sock *sk_to,

int inet_sk_rebuild_header(struct sock *sk);

extern u32 inet_ehash_secret;
extern u32 ipv6_hash_secret;
void build_ehash_secret(void);

static inline unsigned int inet_ehashfn(struct net *net,
const __be32 laddr, const __u16 lport,
const __be32 faddr, const __be16 fport)
static inline unsigned int __inet_ehashfn(const __be32 laddr,
const __u16 lport,
const __be32 faddr,
const __be16 fport,
u32 initval)
{
return jhash_3words((__force __u32) laddr,
(__force __u32) faddr,
((__u32) lport) << 16 | (__force __u32)fport,
inet_ehash_secret + net_hash_mix(net));
}

static inline int inet_sk_ehashfn(const struct sock *sk)
{
const struct inet_sock *inet = inet_sk(sk);
const __be32 laddr = inet->inet_rcv_saddr;
const __u16 lport = inet->inet_num;
const __be32 faddr = inet->inet_daddr;
const __be16 fport = inet->inet_dport;
struct net *net = sock_net(sk);

return inet_ehashfn(net, laddr, lport, faddr, fport);
initval);
}

static inline struct request_sock *inet_reqsk_alloc(struct request_sock_ops *ops)
Expand Down
4 changes: 2 additions & 2 deletions include/net/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -539,14 +539,14 @@ static inline u32 ipv6_addr_hash(const struct in6_addr *a)
}

/* more secured version of ipv6_addr_hash() */
static inline u32 ipv6_addr_jhash(const struct in6_addr *a)
static inline u32 __ipv6_addr_jhash(const struct in6_addr *a, const u32 initval)
{
u32 v = (__force u32)a->s6_addr32[0] ^ (__force u32)a->s6_addr32[1];

return jhash_3words(v,
(__force u32)a->s6_addr32[2],
(__force u32)a->s6_addr32[3],
ipv6_hash_secret);
initval);
}

static inline bool ipv6_addr_loopback(const struct in6_addr *a)
Expand Down
3 changes: 1 addition & 2 deletions include/net/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,6 @@ int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size);
void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb);

/* From syncookies.c */
extern __u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS];
int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th,
u32 cookie);
struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
Expand Down Expand Up @@ -1323,7 +1322,7 @@ extern struct tcp_fastopen_context __rcu *tcp_fastopen_ctx;
int tcp_fastopen_reset_cipher(void *key, unsigned int len);
void tcp_fastopen_cookie_gen(__be32 src, __be32 dst,
struct tcp_fastopen_cookie *foc);

void tcp_fastopen_init_key_once(bool publish);
#define TCP_FASTOPEN_KEY_LENGTH 16

/* Fastopen key context */
Expand Down
7 changes: 7 additions & 0 deletions init/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,13 @@ static char *static_command_line;
static char *execute_command;
static char *ramdisk_execute_command;

/*
* Used to generate warnings if static_key manipulation functions are used
* before jump_label_init is called.
*/
bool static_key_initialized __read_mostly = false;
EXPORT_SYMBOL_GPL(static_key_initialized);

/*
* If set, this is an indication to the drivers that reset the underlying
* device before going ahead with the initialization otherwise driver might
Expand Down
5 changes: 5 additions & 0 deletions kernel/jump_label.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ static void jump_label_update(struct static_key *key, int enable);

void static_key_slow_inc(struct static_key *key)
{
STATIC_KEY_CHECK_USE();
if (atomic_inc_not_zero(&key->enabled))
return;

Expand Down Expand Up @@ -103,19 +104,22 @@ static void jump_label_update_timeout(struct work_struct *work)

void static_key_slow_dec(struct static_key *key)
{
STATIC_KEY_CHECK_USE();
__static_key_slow_dec(key, 0, NULL);
}
EXPORT_SYMBOL_GPL(static_key_slow_dec);

void static_key_slow_dec_deferred(struct static_key_deferred *key)
{
STATIC_KEY_CHECK_USE();
__static_key_slow_dec(&key->key, key->timeout, &key->work);
}
EXPORT_SYMBOL_GPL(static_key_slow_dec_deferred);

void jump_label_rate_limit(struct static_key_deferred *key,
unsigned long rl)
{
STATIC_KEY_CHECK_USE();
key->timeout = rl;
INIT_DELAYED_WORK(&key->work, jump_label_update_timeout);
}
Expand Down Expand Up @@ -212,6 +216,7 @@ void __init jump_label_init(void)
key->next = NULL;
#endif
}
static_key_initialized = true;
jump_label_unlock();
}

Expand Down
14 changes: 2 additions & 12 deletions net/core/secure_seq.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <linux/hrtimer.h>
#include <linux/ktime.h>
#include <linux/string.h>
#include <linux/net.h>

#include <net/secure_seq.h>

Expand All @@ -16,18 +17,7 @@ static u32 net_secret[NET_SECRET_SIZE] ____cacheline_aligned;

static void net_secret_init(void)
{
u32 tmp;
int i;

if (likely(net_secret[0]))
return;

for (i = NET_SECRET_SIZE; i > 0;) {
do {
get_random_bytes(&tmp, sizeof(tmp));
} while (!tmp);
cmpxchg(&net_secret[--i], 0, tmp);
}
net_get_random_once(net_secret, sizeof(net_secret));
}

#ifdef CONFIG_INET
Expand Down
48 changes: 48 additions & 0 deletions net/core/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,51 @@ void inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb,
csum_unfold(*sum)));
}
EXPORT_SYMBOL(inet_proto_csum_replace16);

struct __net_random_once_work {
struct work_struct work;
struct static_key *key;
};

static void __net_random_once_deferred(struct work_struct *w)
{
struct __net_random_once_work *work =
container_of(w, struct __net_random_once_work, work);
if (!static_key_enabled(work->key))
static_key_slow_inc(work->key);
kfree(work);
}

static void __net_random_once_disable_jump(struct static_key *key)
{
struct __net_random_once_work *w;

w = kmalloc(sizeof(*w), GFP_ATOMIC);
if (!w)
return;

INIT_WORK(&w->work, __net_random_once_deferred);
w->key = key;
schedule_work(&w->work);
}

bool __net_get_random_once(void *buf, int nbytes, bool *done,
struct static_key *done_key)
{
static DEFINE_SPINLOCK(lock);

spin_lock_bh(&lock);
if (*done) {
spin_unlock_bh(&lock);
return false;
}

get_random_bytes(buf, nbytes);
*done = true;
spin_unlock_bh(&lock);

__net_random_once_disable_jump(done_key);

return true;
}
EXPORT_SYMBOL(__net_get_random_once);
Loading

0 comments on commit 7dcade3

Please sign in to comment.