Commit 2ca9bf45 authored by Tetsuo Handa's avatar Tetsuo Handa Committed by James Morris

TOMOYO: Allow using executable's realpath and symlink's target as conditions.

This patch adds support for permission checks using executable file's realpath
upon execve() and symlink's target upon symlink(). Hooks are in the last patch
of this pathset.
Signed-off-by: default avatarTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
parent 8761afd4
...@@ -140,6 +140,8 @@ char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt, ...@@ -140,6 +140,8 @@ char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
{ {
char *buf = NULL; char *buf = NULL;
const char *header = NULL; const char *header = NULL;
char *realpath = NULL;
const char *symlink = NULL;
int pos; int pos;
const char *domainname = r->domain->domainname->name; const char *domainname = r->domain->domainname->name;
header = tomoyo_print_header(r); header = tomoyo_print_header(r);
...@@ -147,15 +149,34 @@ char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt, ...@@ -147,15 +149,34 @@ char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
return NULL; return NULL;
/* +10 is for '\n' etc. and '\0'. */ /* +10 is for '\n' etc. and '\0'. */
len += strlen(domainname) + strlen(header) + 10; len += strlen(domainname) + strlen(header) + 10;
if (r->ee) {
struct file *file = r->ee->bprm->file;
realpath = tomoyo_realpath_from_path(&file->f_path);
if (!realpath)
goto out;
/* +80 is for " exec={ realpath=\"%s\" }" */
len += strlen(realpath) + 80;
} else if (r->obj && r->obj->symlink_target) {
symlink = r->obj->symlink_target->name;
/* +18 is for " symlink.target=\"%s\"" */
len += 18 + strlen(symlink);
}
len = tomoyo_round2(len); len = tomoyo_round2(len);
buf = kzalloc(len, GFP_NOFS); buf = kzalloc(len, GFP_NOFS);
if (!buf) if (!buf)
goto out; goto out;
len--; len--;
pos = snprintf(buf, len, "%s", header); pos = snprintf(buf, len, "%s", header);
if (realpath) {
pos += snprintf(buf + pos, len - pos,
" exec={ realpath=\"%s\" }", realpath);
} else if (symlink)
pos += snprintf(buf + pos, len - pos, " symlink.target=\"%s\"",
symlink);
pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname); pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname);
vsnprintf(buf + pos, len - pos, fmt, args); vsnprintf(buf + pos, len - pos, fmt, args);
out: out:
kfree(realpath);
kfree(header); kfree(header);
return buf; return buf;
} }
......
...@@ -79,6 +79,8 @@ const char * const tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = { ...@@ -79,6 +79,8 @@ const char * const tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = {
[TOMOYO_MODE_OTHERS_READ] = "others_read", [TOMOYO_MODE_OTHERS_READ] = "others_read",
[TOMOYO_MODE_OTHERS_WRITE] = "others_write", [TOMOYO_MODE_OTHERS_WRITE] = "others_write",
[TOMOYO_MODE_OTHERS_EXECUTE] = "others_execute", [TOMOYO_MODE_OTHERS_EXECUTE] = "others_execute",
[TOMOYO_EXEC_REALPATH] = "exec.realpath",
[TOMOYO_SYMLINK_TARGET] = "symlink.target",
[TOMOYO_PATH1_UID] = "path1.uid", [TOMOYO_PATH1_UID] = "path1.uid",
[TOMOYO_PATH1_GID] = "path1.gid", [TOMOYO_PATH1_GID] = "path1.gid",
[TOMOYO_PATH1_INO] = "path1.ino", [TOMOYO_PATH1_INO] = "path1.ino",
...@@ -352,6 +354,27 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head, ...@@ -352,6 +354,27 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head,
} }
} }
/**
* tomoyo_print_name_union_quoted - Print a tomoyo_name_union with a quote.
*
* @head: Pointer to "struct tomoyo_io_buffer".
* @ptr: Pointer to "struct tomoyo_name_union".
*
* Returns nothing.
*/
static void tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head,
const struct tomoyo_name_union *ptr)
{
if (ptr->group) {
tomoyo_set_string(head, "@");
tomoyo_set_string(head, ptr->group->group_name->name);
} else {
tomoyo_set_string(head, "\"");
tomoyo_set_string(head, ptr->filename->name);
tomoyo_set_string(head, "\"");
}
}
/** /**
* tomoyo_print_number_union_nospace - Print a tomoyo_number_union without a space. * tomoyo_print_number_union_nospace - Print a tomoyo_number_union without a space.
* *
...@@ -1101,6 +1124,9 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, ...@@ -1101,6 +1124,9 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
(typeof(condp)) (cond + 1); (typeof(condp)) (cond + 1);
const struct tomoyo_number_union *numbers_p = const struct tomoyo_number_union *numbers_p =
(typeof(numbers_p)) (condp + condc); (typeof(numbers_p)) (condp + condc);
const struct tomoyo_name_union *names_p =
(typeof(names_p))
(numbers_p + cond->numbers_count);
u16 skip; u16 skip;
for (skip = 0; skip < head->r.cond_index; skip++) { for (skip = 0; skip < head->r.cond_index; skip++) {
const u8 left = condp->left; const u8 left = condp->left;
...@@ -1112,6 +1138,9 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, ...@@ -1112,6 +1138,9 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
break; break;
} }
switch (right) { switch (right) {
case TOMOYO_NAME_UNION:
names_p++;
break;
case TOMOYO_NUMBER_UNION: case TOMOYO_NUMBER_UNION:
numbers_p++; numbers_p++;
break; break;
...@@ -1138,6 +1167,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, ...@@ -1138,6 +1167,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
} }
tomoyo_set_string(head, match ? "=" : "!="); tomoyo_set_string(head, match ? "=" : "!=");
switch (right) { switch (right) {
case TOMOYO_NAME_UNION:
tomoyo_print_name_union_quoted
(head, names_p++);
break;
case TOMOYO_NUMBER_UNION: case TOMOYO_NUMBER_UNION:
tomoyo_print_number_union_nospace tomoyo_print_number_union_nospace
(head, numbers_p++); (head, numbers_p++);
...@@ -1665,6 +1698,22 @@ static DEFINE_SPINLOCK(tomoyo_query_list_lock); ...@@ -1665,6 +1698,22 @@ static DEFINE_SPINLOCK(tomoyo_query_list_lock);
*/ */
static atomic_t tomoyo_query_observers = ATOMIC_INIT(0); static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
/**
* tomoyo_truncate - Truncate a line.
*
* @str: String to truncate.
*
* Returns length of truncated @str.
*/
static int tomoyo_truncate(char *str)
{
char *start = str;
while (*(unsigned char *) str > (unsigned char) ' ')
str++;
*str = '\0';
return strlen(start) + 1;
}
/** /**
* tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode. * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode.
* *
...@@ -1676,6 +1725,8 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0); ...@@ -1676,6 +1725,8 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header) static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
{ {
char *buffer; char *buffer;
char *realpath = NULL;
char *symlink = NULL;
char *cp = strchr(header, '\n'); char *cp = strchr(header, '\n');
int len; int len;
if (!cp) if (!cp)
...@@ -1685,10 +1736,25 @@ static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header) ...@@ -1685,10 +1736,25 @@ static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
return; return;
*cp++ = '\0'; *cp++ = '\0';
len = strlen(cp) + 1; len = strlen(cp) + 1;
/* strstr() will return NULL if ordering is wrong. */
if (*cp == 'f') {
realpath = strstr(header, " exec={ realpath=\"");
if (realpath) {
realpath += 8;
len += tomoyo_truncate(realpath) + 6;
}
symlink = strstr(header, " symlink.target=\"");
if (symlink)
len += tomoyo_truncate(symlink + 1) + 1;
}
buffer = kmalloc(len, GFP_NOFS); buffer = kmalloc(len, GFP_NOFS);
if (!buffer) if (!buffer)
return; return;
snprintf(buffer, len - 1, "%s", cp); snprintf(buffer, len - 1, "%s", cp);
if (realpath)
tomoyo_addprintf(buffer, len, " exec.%s", realpath);
if (symlink)
tomoyo_addprintf(buffer, len, "%s", symlink);
tomoyo_normalize_line(buffer); tomoyo_normalize_line(buffer);
if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer, if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer,
false)) false))
......
...@@ -73,6 +73,8 @@ enum tomoyo_conditions_index { ...@@ -73,6 +73,8 @@ enum tomoyo_conditions_index {
TOMOYO_MODE_OTHERS_READ, /* S_IROTH */ TOMOYO_MODE_OTHERS_READ, /* S_IROTH */
TOMOYO_MODE_OTHERS_WRITE, /* S_IWOTH */ TOMOYO_MODE_OTHERS_WRITE, /* S_IWOTH */
TOMOYO_MODE_OTHERS_EXECUTE, /* S_IXOTH */ TOMOYO_MODE_OTHERS_EXECUTE, /* S_IXOTH */
TOMOYO_EXEC_REALPATH,
TOMOYO_SYMLINK_TARGET,
TOMOYO_PATH1_UID, TOMOYO_PATH1_UID,
TOMOYO_PATH1_GID, TOMOYO_PATH1_GID,
TOMOYO_PATH1_INO, TOMOYO_PATH1_INO,
...@@ -101,6 +103,7 @@ enum tomoyo_conditions_index { ...@@ -101,6 +103,7 @@ enum tomoyo_conditions_index {
TOMOYO_PATH2_PARENT_PERM, TOMOYO_PATH2_PARENT_PERM,
TOMOYO_MAX_CONDITION_KEYWORD, TOMOYO_MAX_CONDITION_KEYWORD,
TOMOYO_NUMBER_UNION, TOMOYO_NUMBER_UNION,
TOMOYO_NAME_UNION,
}; };
...@@ -351,6 +354,11 @@ struct tomoyo_request_info { ...@@ -351,6 +354,11 @@ struct tomoyo_request_info {
* NULL if not dealing files. * NULL if not dealing files.
*/ */
struct tomoyo_obj_info *obj; struct tomoyo_obj_info *obj;
/*
* For holding parameters specific to execve() request.
* NULL if not dealing do_execve().
*/
struct tomoyo_execve *ee;
struct tomoyo_domain_info *domain; struct tomoyo_domain_info *domain;
/* For holding parameters. */ /* For holding parameters. */
union { union {
...@@ -476,6 +484,20 @@ struct tomoyo_obj_info { ...@@ -476,6 +484,20 @@ struct tomoyo_obj_info {
* parent directory. * parent directory.
*/ */
struct tomoyo_mini_stat stat[TOMOYO_MAX_PATH_STAT]; struct tomoyo_mini_stat stat[TOMOYO_MAX_PATH_STAT];
/*
* Content of symbolic link to be created. NULL for operations other
* than symlink().
*/
struct tomoyo_path_info *symlink_target;
};
/* Structure for execve() operation. */
struct tomoyo_execve {
struct tomoyo_request_info r;
struct tomoyo_obj_info obj;
struct linux_binprm *bprm;
/* For temporary use. */
char *tmp; /* Size is TOMOYO_EXEC_TMPSIZE bytes */
}; };
/* Structure for entries which follows "struct tomoyo_condition". */ /* Structure for entries which follows "struct tomoyo_condition". */
...@@ -494,9 +516,11 @@ struct tomoyo_condition { ...@@ -494,9 +516,11 @@ struct tomoyo_condition {
u32 size; /* Memory size allocated for this entry. */ u32 size; /* Memory size allocated for this entry. */
u16 condc; /* Number of conditions in this struct. */ u16 condc; /* Number of conditions in this struct. */
u16 numbers_count; /* Number of "struct tomoyo_number_union values". */ u16 numbers_count; /* Number of "struct tomoyo_number_union values". */
u16 names_count; /* Number of "struct tomoyo_name_union names". */
/* /*
* struct tomoyo_condition_element condition[condc]; * struct tomoyo_condition_element condition[condc];
* struct tomoyo_number_union values[numbers_count]; * struct tomoyo_number_union values[numbers_count];
* struct tomoyo_name_union names[names_count];
*/ */
}; };
......
...@@ -10,6 +10,68 @@ ...@@ -10,6 +10,68 @@
/* List of "struct tomoyo_condition". */ /* List of "struct tomoyo_condition". */
LIST_HEAD(tomoyo_condition_list); LIST_HEAD(tomoyo_condition_list);
/**
* tomoyo_scan_exec_realpath - Check "exec.realpath" parameter of "struct tomoyo_condition".
*
* @file: Pointer to "struct file".
* @ptr: Pointer to "struct tomoyo_name_union".
* @match: True if "exec.realpath=", false if "exec.realpath!=".
*
* Returns true on success, false otherwise.
*/
static bool tomoyo_scan_exec_realpath(struct file *file,
const struct tomoyo_name_union *ptr,
const bool match)
{
bool result;
struct tomoyo_path_info exe;
if (!file)
return false;
exe.name = tomoyo_realpath_from_path(&file->f_path);
if (!exe.name)
return false;
tomoyo_fill_path_info(&exe);
result = tomoyo_compare_name_union(&exe, ptr);
kfree(exe.name);
return result == match;
}
/**
* tomoyo_get_dqword - tomoyo_get_name() for a quoted string.
*
* @start: String to save.
*
* Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
*/
static const struct tomoyo_path_info *tomoyo_get_dqword(char *start)
{
char *cp = start + strlen(start) - 1;
if (cp == start || *start++ != '"' || *cp != '"')
return NULL;
*cp = '\0';
if (*start && !tomoyo_correct_word(start))
return NULL;
return tomoyo_get_name(start);
}
/**
* tomoyo_parse_name_union_quoted - Parse a quoted word.
*
* @param: Pointer to "struct tomoyo_acl_param".
* @ptr: Pointer to "struct tomoyo_name_union".
*
* Returns true on success, false otherwise.
*/
static bool tomoyo_parse_name_union_quoted(struct tomoyo_acl_param *param,
struct tomoyo_name_union *ptr)
{
char *filename = param->data;
if (*filename == '@')
return tomoyo_parse_name_union(param, ptr);
ptr->filename = tomoyo_get_dqword(filename);
return ptr->filename != NULL;
}
/** /**
* tomoyo_same_condition - Check for duplicated "struct tomoyo_condition" entry. * tomoyo_same_condition - Check for duplicated "struct tomoyo_condition" entry.
* *
...@@ -23,6 +85,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a, ...@@ -23,6 +85,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a,
{ {
return a->size == b->size && a->condc == b->condc && return a->size == b->size && a->condc == b->condc &&
a->numbers_count == b->numbers_count && a->numbers_count == b->numbers_count &&
a->names_count == b->names_count &&
!memcmp(a + 1, b + 1, a->size - sizeof(*a)); !memcmp(a + 1, b + 1, a->size - sizeof(*a));
} }
...@@ -114,6 +177,7 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param) ...@@ -114,6 +177,7 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
struct tomoyo_condition *entry = NULL; struct tomoyo_condition *entry = NULL;
struct tomoyo_condition_element *condp = NULL; struct tomoyo_condition_element *condp = NULL;
struct tomoyo_number_union *numbers_p = NULL; struct tomoyo_number_union *numbers_p = NULL;
struct tomoyo_name_union *names_p = NULL;
struct tomoyo_condition e = { }; struct tomoyo_condition e = { };
char * const start_of_string = param->data; char * const start_of_string = param->data;
char * const end_of_string = start_of_string + strlen(start_of_string); char * const end_of_string = start_of_string + strlen(start_of_string);
...@@ -178,6 +242,20 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param) ...@@ -178,6 +242,20 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
e.condc++; e.condc++;
else else
e.condc--; e.condc--;
if (left == TOMOYO_EXEC_REALPATH ||
left == TOMOYO_SYMLINK_TARGET) {
if (!names_p) {
e.names_count++;
} else {
e.names_count--;
right = TOMOYO_NAME_UNION;
param->data = right_word;
if (!tomoyo_parse_name_union_quoted(param,
names_p++))
goto out;
}
goto store_value;
}
right = tomoyo_condition_type(right_word); right = tomoyo_condition_type(right_word);
if (right == TOMOYO_MAX_CONDITION_KEYWORD) { if (right == TOMOYO_MAX_CONDITION_KEYWORD) {
if (!numbers_p) { if (!numbers_p) {
...@@ -191,6 +269,7 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param) ...@@ -191,6 +269,7 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
goto out; goto out;
} }
} }
store_value:
if (!condp) { if (!condp) {
dprintk(KERN_WARNING "%u: dry_run left=%u right=%u " dprintk(KERN_WARNING "%u: dry_run left=%u right=%u "
"match=%u\n", __LINE__, left, right, !is_not); "match=%u\n", __LINE__, left, right, !is_not);
...@@ -204,21 +283,23 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param) ...@@ -204,21 +283,23 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
condp->equals); condp->equals);
condp++; condp++;
} }
dprintk(KERN_INFO "%u: cond=%u numbers=%u\n", dprintk(KERN_INFO "%u: cond=%u numbers=%u names=%u\n",
__LINE__, e.condc, e.numbers_count); __LINE__, e.condc, e.numbers_count, e.names_count);
if (entry) { if (entry) {
BUG_ON(e.numbers_count | e.condc); BUG_ON(e.names_count | e.numbers_count | e.condc);
return tomoyo_commit_condition(entry); return tomoyo_commit_condition(entry);
} }
e.size = sizeof(*entry) e.size = sizeof(*entry)
+ e.condc * sizeof(struct tomoyo_condition_element) + e.condc * sizeof(struct tomoyo_condition_element)
+ e.numbers_count * sizeof(struct tomoyo_number_union); + e.numbers_count * sizeof(struct tomoyo_number_union)
+ e.names_count * sizeof(struct tomoyo_name_union);
entry = kzalloc(e.size, GFP_NOFS); entry = kzalloc(e.size, GFP_NOFS);
if (!entry) if (!entry)
return NULL; return NULL;
*entry = e; *entry = e;
condp = (struct tomoyo_condition_element *) (entry + 1); condp = (struct tomoyo_condition_element *) (entry + 1);
numbers_p = (struct tomoyo_number_union *) (condp + e.condc); numbers_p = (struct tomoyo_number_union *) (condp + e.condc);
names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count);
{ {
bool flag = false; bool flag = false;
for (pos = start_of_string; pos < end_of_string; pos++) { for (pos = start_of_string; pos < end_of_string; pos++) {
...@@ -309,6 +390,7 @@ bool tomoyo_condition(struct tomoyo_request_info *r, ...@@ -309,6 +390,7 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
unsigned long max_v[2] = { 0, 0 }; unsigned long max_v[2] = { 0, 0 };
const struct tomoyo_condition_element *condp; const struct tomoyo_condition_element *condp;
const struct tomoyo_number_union *numbers_p; const struct tomoyo_number_union *numbers_p;
const struct tomoyo_name_union *names_p;
struct tomoyo_obj_info *obj; struct tomoyo_obj_info *obj;
u16 condc; u16 condc;
if (!cond) if (!cond)
...@@ -317,6 +399,8 @@ bool tomoyo_condition(struct tomoyo_request_info *r, ...@@ -317,6 +399,8 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
obj = r->obj; obj = r->obj;
condp = (struct tomoyo_condition_element *) (cond + 1); condp = (struct tomoyo_condition_element *) (cond + 1);
numbers_p = (const struct tomoyo_number_union *) (condp + condc); numbers_p = (const struct tomoyo_number_union *) (condp + condc);
names_p = (const struct tomoyo_name_union *)
(numbers_p + cond->numbers_count);
for (i = 0; i < condc; i++) { for (i = 0; i < condc; i++) {
const bool match = condp->equals; const bool match = condp->equals;
const u8 left = condp->left; const u8 left = condp->left;
...@@ -324,6 +408,30 @@ bool tomoyo_condition(struct tomoyo_request_info *r, ...@@ -324,6 +408,30 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
bool is_bitop[2] = { false, false }; bool is_bitop[2] = { false, false };
u8 j; u8 j;
condp++; condp++;
/* Check string expressions. */
if (right == TOMOYO_NAME_UNION) {
const struct tomoyo_name_union *ptr = names_p++;
switch (left) {
struct tomoyo_path_info *symlink;
struct tomoyo_execve *ee;
struct file *file;
case TOMOYO_SYMLINK_TARGET:
symlink = obj ? obj->symlink_target : NULL;
if (!symlink ||
!tomoyo_compare_name_union(symlink, ptr)
== match)
goto out;
break;
case TOMOYO_EXEC_REALPATH:
ee = r->ee;
file = ee ? ee->bprm->file : NULL;
if (!tomoyo_scan_exec_realpath(file, ptr,
match))
goto out;
break;
}
continue;
}
/* Check numeric or bit-op expressions. */ /* Check numeric or bit-op expressions. */
for (j = 0; j < 2; j++) { for (j = 0; j < 2; j++) {
const u8 index = j ? right : left; const u8 index = j ? right : left;
......
...@@ -357,13 +357,18 @@ void tomoyo_del_condition(struct list_head *element) ...@@ -357,13 +357,18 @@ void tomoyo_del_condition(struct list_head *element)
head.list); head.list);
const u16 condc = cond->condc; const u16 condc = cond->condc;
const u16 numbers_count = cond->numbers_count; const u16 numbers_count = cond->numbers_count;
const u16 names_count = cond->names_count;
unsigned int i; unsigned int i;
const struct tomoyo_condition_element *condp const struct tomoyo_condition_element *condp
= (const struct tomoyo_condition_element *) (cond + 1); = (const struct tomoyo_condition_element *) (cond + 1);
struct tomoyo_number_union *numbers_p struct tomoyo_number_union *numbers_p
= (struct tomoyo_number_union *) (condp + condc); = (struct tomoyo_number_union *) (condp + condc);
struct tomoyo_name_union *names_p
= (struct tomoyo_name_union *) (numbers_p + numbers_count);
for (i = 0; i < numbers_count; i++) for (i = 0; i < numbers_count; i++)
tomoyo_put_number_union(numbers_p++); tomoyo_put_number_union(numbers_p++);
for (i = 0; i < names_count; i++)
tomoyo_put_name_union(names_p++);
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment