Release 4.11 security/selinux/ss/conditional.c
/* Authors: Karl MacMillan <kmacmillan@tresys.com>
* Frank Mayer <mayerf@tresys.com>
*
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include "security.h"
#include "conditional.h"
#include "services.h"
/*
* cond_evaluate_expr evaluates a conditional expr
* in reverse polish notation. It returns true (1), false (0),
* or undefined (-1). Undefined occurs when the expression
* exceeds the stack depth of COND_EXPR_MAXDEPTH.
*/
static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr)
{
struct cond_expr *cur;
int s[COND_EXPR_MAXDEPTH];
int sp = -1;
for (cur = expr; cur; cur = cur->next) {
switch (cur->expr_type) {
case COND_BOOL:
if (sp == (COND_EXPR_MAXDEPTH - 1))
return -1;
sp++;
s[sp] = p->bool_val_to_struct[cur->bool - 1]->state;
break;
case COND_NOT:
if (sp < 0)
return -1;
s[sp] = !s[sp];
break;
case COND_OR:
if (sp < 1)
return -1;
sp--;
s[sp] |= s[sp + 1];
break;
case COND_AND:
if (sp < 1)
return -1;
sp--;
s[sp] &= s[sp + 1];
break;
case COND_XOR:
if (sp < 1)
return -1;
sp--;
s[sp] ^= s[sp + 1];
break;
case COND_EQ:
if (sp < 1)
return -1;
sp--;
s[sp] = (s[sp] == s[sp + 1]);
break;
case COND_NEQ:
if (sp < 1)
return -1;
sp--;
s[sp] = (s[sp] != s[sp + 1]);
break;
default:
return -1;
}
}
return s[0];
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 291 | 100.00% | 1 | 100.00% |
Total | 291 | 100.00% | 1 | 100.00% |
/*
* evaluate_cond_node evaluates the conditional stored in
* a struct cond_node and if the result is different than the
* current state of the node it sets the rules in the true/false
* list appropriately. If the result of the expression is undefined
* all of the rules are disabled for safety.
*/
int evaluate_cond_node(struct policydb *p, struct cond_node *node)
{
int new_state;
struct cond_av_list *cur;
new_state = cond_evaluate_expr(p, node->expr);
if (new_state != node->cur_state) {
node->cur_state = new_state;
if (new_state == -1)
printk(KERN_ERR "SELinux: expression result was undefined - disabling all rules.\n");
/* turn the rules on or off */
for (cur = node->true_list; cur; cur = cur->next) {
if (new_state <= 0)
cur->node->key.specified &= ~AVTAB_ENABLED;
else
cur->node->key.specified |= AVTAB_ENABLED;
}
for (cur = node->false_list; cur; cur = cur->next) {
/* -1 or 1 */
if (new_state)
cur->node->key.specified &= ~AVTAB_ENABLED;
else
cur->node->key.specified |= AVTAB_ENABLED;
}
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 153 | 96.84% | 1 | 33.33% |
Stephen D. Smalley | 4 | 2.53% | 1 | 33.33% |
James Morris | 1 | 0.63% | 1 | 33.33% |
Total | 158 | 100.00% | 3 | 100.00% |
int cond_policydb_init(struct policydb *p)
{
int rc;
p->bool_val_to_struct = NULL;
p->cond_list = NULL;
rc = avtab_init(&p->te_cond_avtab);
if (rc)
return rc;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 35 | 77.78% | 1 | 50.00% |
Dan Carpenter | 10 | 22.22% | 1 | 50.00% |
Total | 45 | 100.00% | 2 | 100.00% |
static void cond_av_list_destroy(struct cond_av_list *list)
{
struct cond_av_list *cur, *next;
for (cur = list; cur; cur = next) {
next = cur->next;
/* the avtab_ptr_t node is destroy by the avtab */
kfree(cur);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 45 | 100.00% | 1 | 100.00% |
Total | 45 | 100.00% | 1 | 100.00% |
static void cond_node_destroy(struct cond_node *node)
{
struct cond_expr *cur_expr, *next_expr;
for (cur_expr = node->expr; cur_expr; cur_expr = next_expr) {
next_expr = cur_expr->next;
kfree(cur_expr);
}
cond_av_list_destroy(node->true_list);
cond_av_list_destroy(node->false_list);
kfree(node);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 65 | 100.00% | 1 | 100.00% |
Total | 65 | 100.00% | 1 | 100.00% |
static void cond_list_destroy(struct cond_node *list)
{
struct cond_node *next, *cur;
if (list == NULL)
return;
for (cur = list; cur; cur = next) {
next = cur->next;
cond_node_destroy(cur);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 51 | 100.00% | 1 | 100.00% |
Total | 51 | 100.00% | 1 | 100.00% |
void cond_policydb_destroy(struct policydb *p)
{
kfree(p->bool_val_to_struct);
avtab_destroy(&p->te_cond_avtab);
cond_list_destroy(p->cond_list);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 32 | 100.00% | 1 | 100.00% |
Total | 32 | 100.00% | 1 | 100.00% |
int cond_init_bool_indexes(struct policydb *p)
{
kfree(p->bool_val_to_struct);
p->bool_val_to_struct =
kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum *), GFP_KERNEL);
if (!p->bool_val_to_struct)
return -ENOMEM;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 52 | 98.11% | 1 | 50.00% |
Davidlohr Bueso A | 1 | 1.89% | 1 | 50.00% |
Total | 53 | 100.00% | 2 | 100.00% |
int cond_destroy_bool(void *key, void *datum, void *p)
{
kfree(key);
kfree(datum);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 30 | 100.00% | 1 | 100.00% |
Total | 30 | 100.00% | 1 | 100.00% |
int cond_index_bool(void *key, void *datum, void *datap)
{
struct policydb *p;
struct cond_bool_datum *booldatum;
struct flex_array *fa;
booldatum = datum;
p = datap;
if (!booldatum->value || booldatum->value > p->p_bools.nprim)
return -EINVAL;
fa = p->sym_val_to_name[SYM_BOOLS];
if (flex_array_put_ptr(fa, booldatum->value - 1, key,
GFP_KERNEL | __GFP_ZERO))
BUG();
p->bool_val_to_struct[booldatum->value - 1] = booldatum;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 82 | 75.93% | 1 | 50.00% |
Eric Paris | 26 | 24.07% | 1 | 50.00% |
Total | 108 | 100.00% | 2 | 100.00% |
static int bool_isvalid(struct cond_bool_datum *b)
{
if (!(b->state == 0 || b->state == 1))
return 0;
return 1;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 33 | 97.06% | 1 | 50.00% |
Stephen D. Smalley | 1 | 2.94% | 1 | 50.00% |
Total | 34 | 100.00% | 2 | 100.00% |
int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp)
{
char *key = NULL;
struct cond_bool_datum *booldatum;
__le32 buf[3];
u32 len;
int rc;
booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL);
if (!booldatum)
return -ENOMEM;
rc = next_entry(buf, fp, sizeof buf);
if (rc)
goto err;
booldatum->value = le32_to_cpu(buf[0]);
booldatum->state = le32_to_cpu(buf[1]);
rc = -EINVAL;
if (!bool_isvalid(booldatum))
goto err;
len = le32_to_cpu(buf[2]);
if (((len == 0) || (len == (u32)-1)))
goto err;
rc = -ENOMEM;
key = kmalloc(len + 1, GFP_KERNEL);
if (!key)
goto err;
rc = next_entry(key, fp, len);
if (rc)
goto err;
key[len] = '\0';
rc = hashtab_insert(h, key, booldatum);
if (rc)
goto err;
return 0;
err:
cond_destroy_bool(key, booldatum, NULL);
return rc;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 170 | 70.83% | 1 | 12.50% |
William Roberts | 23 | 9.58% | 1 | 12.50% |
Stephen D. Smalley | 22 | 9.17% | 1 | 12.50% |
Dan Carpenter | 18 | 7.50% | 1 | 12.50% |
Alexey Dobriyan | 3 | 1.25% | 1 | 12.50% |
Chris Wright | 2 | 0.83% | 1 | 12.50% |
James Morris | 1 | 0.42% | 1 | 12.50% |
Vesa-Matti J Kari | 1 | 0.42% | 1 | 12.50% |
Total | 240 | 100.00% | 8 | 100.00% |
struct cond_insertf_data {
struct policydb *p;
struct cond_av_list *other;
struct cond_av_list *head;
struct cond_av_list *tail;
};
static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum *d, void *ptr)
{
struct cond_insertf_data *data = ptr;
struct policydb *p = data->p;
struct cond_av_list *other = data->other, *list, *cur;
struct avtab_node *node_ptr;
u8 found;
int rc = -EINVAL;
/*
* For type rules we have to make certain there aren't any
* conflicting rules by searching the te_avtab and the
* cond_te_avtab.
*/
if (k->specified & AVTAB_TYPE) {
if (avtab_search(&p->te_avtab, k)) {
printk(KERN_ERR "SELinux: type rule already exists outside of a conditional.\n");
goto err;
}
/*
* If we are reading the false list other will be a pointer to
* the true list. We can have duplicate entries if there is only
* 1 other entry and it is in our true list.
*
* If we are reading the true list (other == NULL) there shouldn't
* be any other entries.
*/
if (other) {
node_ptr = avtab_search_node(&p->te_cond_avtab, k);
if (node_ptr) {
if (avtab_search_node_next(node_ptr, k->specified)) {
printk(KERN_ERR "SELinux: too many conflicting type rules.\n");
goto err;
}
found = 0;
for (cur = other; cur; cur = cur->next) {
if (cur->node == node_ptr) {
found = 1;
break;
}
}
if (!found) {
printk(KERN_ERR "SELinux: conflicting type rules.\n");
goto err;
}
}
} else {
if (avtab_search(&p->te_cond_avtab, k)) {
printk(KERN_ERR "SELinux: conflicting type rules when adding type rule for true.\n");
goto err;
}
}
}
node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d);
if (!node_ptr) {
printk(KERN_ERR "SELinux: could not insert rule.\n");
rc = -ENOMEM;
goto err;
}
list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL);
if (!list) {
rc = -ENOMEM;
goto err;
}
list->node = node_ptr;
if (!data->head)
data->head = list;
else
data->tail->next = list;
data->tail = list;
return 0;
err:
cond_av_list_destroy(data->head);
data->head = NULL;
return rc;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 246 | 71.10% | 1 | 14.29% |
Stephen D. Smalley | 70 | 20.23% | 2 | 28.57% |
Dan Carpenter | 19 | 5.49% | 1 | 14.29% |
Eric Paris | 8 | 2.31% | 1 | 14.29% |
James Morris | 3 | 0.87% | 2 | 28.57% |
Total | 346 | 100.00% | 7 | 100.00% |
static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other)
{
int i, rc;
__le32 buf[1];
u32 len;
struct cond_insertf_data data;
*ret_list = NULL;
len = 0;
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
return rc;
len = le32_to_cpu(buf[0]);
if (len == 0)
return 0;
data.p = p;
data.other = other;
data.head = NULL;
data.tail = NULL;
for (i = 0; i < len; i++) {
rc = avtab_read_item(&p->te_cond_avtab, fp, p, cond_insertf,
&data);
if (rc)
return rc;
}
*ret_list = data.head;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Stephen D. Smalley | 156 | 92.86% | 2 | 40.00% |
Andrew Morton | 8 | 4.76% | 1 | 20.00% |
Alexey Dobriyan | 3 | 1.79% | 1 | 20.00% |
Dan Carpenter | 1 | 0.60% | 1 | 20.00% |
Total | 168 | 100.00% | 5 | 100.00% |
static int expr_isvalid(struct policydb *p, struct cond_expr *expr)
{
if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) {
printk(KERN_ERR "SELinux: conditional expressions uses unknown operator.\n");
return 0;
}
if (expr->bool > p->p_bools.nprim) {
printk(KERN_ERR "SELinux: conditional expressions uses unknown bool.\n");
return 0;
}
return 1;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 63 | 94.03% | 1 | 33.33% |
James Morris | 2 | 2.99% | 1 | 33.33% |
Eric Paris | 2 | 2.99% | 1 | 33.33% |
Total | 67 | 100.00% | 3 | 100.00% |
static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)
{
__le32 buf[2];
u32 len, i;
int rc;
struct cond_expr *expr = NULL, *last = NULL;
rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
goto err;
node->cur_state = le32_to_cpu(buf[0]);
/* expr */
len = le32_to_cpu(buf[1]);
for (i = 0; i < len; i++) {
rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
goto err;
rc = -ENOMEM;
expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL);
if (!expr)
goto err;
expr->expr_type = le32_to_cpu(buf[0]);
expr->bool = le32_to_cpu(buf[1]);
if (!expr_isvalid(p, expr)) {
rc = -EINVAL;
kfree(expr);
goto err;
}
if (i == 0)
node->expr = expr;
else
last->next = expr;
last = expr;
}
rc = cond_read_av_list(p, fp, &node->true_list, NULL);
if (rc)
goto err;
rc = cond_read_av_list(p, fp, &node->false_list, node->true_list);
if (rc)
goto err;
return 0;
err:
cond_node_destroy(node);
return rc;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 233 | 80.62% | 1 | 12.50% |
Dan Carpenter | 23 | 7.96% | 1 | 12.50% |
Stephen D. Smalley | 16 | 5.54% | 1 | 12.50% |
Alexander Nyberg | 7 | 2.42% | 1 | 12.50% |
Namhyung Kim | 6 | 2.08% | 2 | 25.00% |
Alexey Dobriyan | 3 | 1.04% | 1 | 12.50% |
James Morris | 1 | 0.35% | 1 | 12.50% |
Total | 289 | 100.00% | 8 | 100.00% |
int cond_read_list(struct policydb *p, void *fp)
{
struct cond_node *node, *last = NULL;
__le32 buf[1];
u32 i, len;
int rc;
rc = next_entry(buf, fp, sizeof buf);
if (rc)
return rc;
len = le32_to_cpu(buf[0]);
rc = avtab_alloc(&(p->te_cond_avtab), p->te_avtab.nel);
if (rc)
goto err;
for (i = 0; i < len; i++) {
rc = -ENOMEM;
node = kzalloc(sizeof(struct cond_node), GFP_KERNEL);
if (!node)
goto err;
rc = cond_read_node(p, node, fp);
if (rc)
goto err;
if (i == 0)
p->cond_list = node;
else
last->next = node;
last = node;
}
return 0;
err:
cond_list_destroy(p->cond_list);
p->cond_list = NULL;
return rc;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 136 | 69.74% | 1 | 14.29% |
Yuichi Nakamura | 25 | 12.82% | 1 | 14.29% |
Stephen D. Smalley | 17 | 8.72% | 2 | 28.57% |
Dan Carpenter | 13 | 6.67% | 1 | 14.29% |
Alexey Dobriyan | 3 | 1.54% | 1 | 14.29% |
James Morris | 1 | 0.51% | 1 | 14.29% |
Total | 195 | 100.00% | 7 | 100.00% |
int cond_write_bool(void *vkey, void *datum, void *ptr)
{
char *key = vkey;
struct cond_bool_datum *booldatum = datum;
struct policy_data *pd = ptr;
void *fp = pd->fp;
__le32 buf[3];
u32 len;
int rc;
len = strlen(key);
buf[0] = cpu_to_le32(booldatum->value);
buf[1] = cpu_to_le32(booldatum->state);
buf[2] = cpu_to_le32(len);
rc = put_entry(buf, sizeof(u32), 3, fp);
if (rc)
return rc;
rc = put_entry(key, 1, len, fp);
if (rc)
return rc;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric Paris | 144 | 100.00% | 1 | 100.00% |
Total | 144 | 100.00% | 1 | 100.00% |
/*
* cond_write_cond_av_list doesn't write out the av_list nodes.
* Instead it writes out the key/value pairs from the avtab. This
* is necessary because there is no way to uniquely identifying rules
* in the avtab so it is not possible to associate individual rules
* in the avtab with a conditional without saving them as part of
* the conditional. This means that the avtab with the conditional
* rules will not be saved but will be rebuilt on policy load.
*/
static int cond_write_av_list(struct policydb *p,
struct cond_av_list *list, struct policy_file *fp)
{
__le32 buf[1];
struct cond_av_list *cur_list;
u32 len;
int rc;
len = 0;
for (cur_list = list; cur_list != NULL; cur_list = cur_list->next)
len++;
buf[0] = cpu_to_le32(len);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
if (len == 0)
return 0;
for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) {
rc = avtab_write_item(p, cur_list->node, fp);
if (rc)
return rc;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric Paris | 144 | 100.00% | 1 | 100.00% |
Total | 144 | 100.00% | 1 | 100.00% |
static int cond_write_node(struct policydb *p, struct cond_node *node,
struct policy_file *fp)
{
struct cond_expr *cur_expr;
__le32 buf[2];
int rc;
u32 len = 0;
buf[0] = cpu_to_le32(node->cur_state);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next)
len++;
buf[0] = cpu_to_le32(len);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next) {
buf[0] = cpu_to_le32(cur_expr->expr_type);
buf[1] = cpu_to_le32(cur_expr->bool);
rc = put_entry(buf, sizeof(u32), 2, fp);
if (rc)
return rc;
}
rc = cond_write_av_list(p, node->true_list, fp);
if (rc)
return rc;
rc = cond_write_av_list(p, node->false_list, fp);
if (rc)
return rc;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric Paris | 238 | 99.58% | 1 | 50.00% |
James Morris | 1 | 0.42% | 1 | 50.00% |
Total | 239 | 100.00% | 2 | 100.00% |
int cond_write_list(struct policydb *p, struct cond_node *list, void *fp)
{
struct cond_node *cur;
u32 len;
__le32 buf[1];
int rc;
len = 0;
for (cur = list; cur != NULL; cur = cur->next)
len++;
buf[0] = cpu_to_le32(len);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
return rc;
for (cur = list; cur != NULL; cur = cur->next) {
rc = cond_write_node(p, cur, fp);
if (rc)
return rc;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Eric Paris | 131 | 100.00% | 1 | 100.00% |
Total | 131 | 100.00% | 1 | 100.00% |
void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key,
struct extended_perms_decision *xpermd)
{
struct avtab_node *node;
if (!ctab || !key || !xpermd)
return;
for (node = avtab_search_node(ctab, key); node;
node = avtab_search_node_next(node, key->specified)) {
if (node->key.specified & AVTAB_ENABLED)
services_compute_xperms_decision(xpermd, node);
}
return;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jeff Vander Stoep | 81 | 100.00% | 1 | 100.00% |
Total | 81 | 100.00% | 1 | 100.00% |
/* Determine whether additional permissions are granted by the conditional
* av table, and if so, add them to the result
*/
void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
struct av_decision *avd, struct extended_perms *xperms)
{
struct avtab_node *node;
if (!ctab || !key || !avd)
return;
for (node = avtab_search_node(ctab, key); node;
node = avtab_search_node_next(node, key->specified)) {
if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) ==
(node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED)))
avd->allowed |= node->datum.u.data;
if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) ==
(node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED)))
/* Since a '0' in an auditdeny mask represents a
* permission we do NOT want to audit (dontaudit), we use
* the '&' operand to ensure that all '0's in the mask
* are retained (much unlike the allow and auditallow cases).
*/
avd->auditdeny &= node->datum.u.data;
if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) ==
(node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))
avd->auditallow |= node->datum.u.data;
if (xperms && (node->key.specified & AVTAB_ENABLED) &&
(node->key.specified & AVTAB_XPERMS))
services_compute_xperms_drivers(xperms, node);
}
return;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 155 | 73.11% | 1 | 25.00% |
Jeff Vander Stoep | 40 | 18.87% | 1 | 25.00% |
Stephen D. Smalley | 17 | 8.02% | 2 | 50.00% |
Total | 212 | 100.00% | 4 | 100.00% |
Overall Contributors
Person | Tokens | Prop | Commits | CommitProp |
Andrew Morton | 1904 | 59.09% | 1 | 3.57% |
Eric Paris | 695 | 21.57% | 4 | 14.29% |
Stephen D. Smalley | 328 | 10.18% | 5 | 17.86% |
Jeff Vander Stoep | 124 | 3.85% | 1 | 3.57% |
Dan Carpenter | 84 | 2.61% | 5 | 17.86% |
Yuichi Nakamura | 25 | 0.78% | 1 | 3.57% |
William Roberts | 23 | 0.71% | 1 | 3.57% |
Alexey Dobriyan | 12 | 0.37% | 1 | 3.57% |
James Morris | 10 | 0.31% | 3 | 10.71% |
Alexander Nyberg | 7 | 0.22% | 1 | 3.57% |
Namhyung Kim | 6 | 0.19% | 2 | 7.14% |
Chris Wright | 2 | 0.06% | 1 | 3.57% |
Davidlohr Bueso A | 1 | 0.03% | 1 | 3.57% |
Vesa-Matti J Kari | 1 | 0.03% | 1 | 3.57% |
Total | 3222 | 100.00% | 28 | 100.00% |
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.