| /* |
| * Copyright 2011 Tresys Technology, LLC. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY TRESYS TECHNOLOGY, LLC ``AS IS'' AND ANY EXPRESS |
| * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| * EVENT SHALL TRESYS TECHNOLOGY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, |
| * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * The views and conclusions contained in the software and documentation are those |
| * of the authors and should not be interpreted as representing official policies, |
| * either expressed or implied, of Tresys Technology, LLC. |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdint.h> |
| #include <sepol/errcodes.h> |
| |
| #include "cil_internal.h" |
| #include "cil_log.h" |
| #include "cil_mem.h" |
| #include "cil_tree.h" |
| #include "cil_lexer.h" |
| #include "cil_parser.h" |
| #include "cil_strpool.h" |
| #include "cil_stack.h" |
| |
| #define CIL_PARSER_MAX_EXPR_DEPTH (0x1 << 12) |
| |
| char *CIL_KEY_HLL_LMS; |
| char *CIL_KEY_HLL_LMX; |
| char *CIL_KEY_HLL_LME; |
| |
| struct hll_info { |
| uint32_t hll_lineno; |
| uint32_t hll_expand; |
| }; |
| |
| static void push_hll_info(struct cil_stack *stack, uint32_t hll_lineno, uint32_t hll_expand) |
| { |
| struct hll_info *new = cil_malloc(sizeof(*new)); |
| |
| new->hll_lineno = hll_lineno; |
| new->hll_expand = hll_expand; |
| |
| cil_stack_push(stack, CIL_NONE, new); |
| } |
| |
| static void pop_hll_info(struct cil_stack *stack, uint32_t *hll_lineno, uint32_t *hll_expand) |
| { |
| struct cil_stack_item *curr = cil_stack_pop(stack); |
| struct cil_stack_item *prev = cil_stack_peek(stack); |
| struct hll_info *old; |
| |
| free(curr->data); |
| |
| if (!prev) { |
| *hll_lineno = 0; |
| *hll_expand = 0; |
| } else { |
| old = prev->data; |
| *hll_lineno = old->hll_lineno; |
| *hll_expand = old->hll_expand; |
| } |
| } |
| |
| static void create_node(struct cil_tree_node **node, struct cil_tree_node *current, uint32_t line, uint32_t hll_line, void *value) |
| { |
| cil_tree_node_init(node); |
| (*node)->parent = current; |
| (*node)->flavor = CIL_NODE; |
| (*node)->line = line; |
| (*node)->hll_line = hll_line; |
| (*node)->data = value; |
| } |
| |
| static void insert_node(struct cil_tree_node *node, struct cil_tree_node *current) |
| { |
| if (current->cl_head == NULL) { |
| current->cl_head = node; |
| } else { |
| current->cl_tail->next = node; |
| } |
| current->cl_tail = node; |
| } |
| |
| static int add_hll_linemark(struct cil_tree_node **current, uint32_t *hll_lineno, uint32_t *hll_expand, struct cil_stack *stack, char *path) |
| { |
| char *hll_type; |
| struct cil_tree_node *node; |
| struct token tok; |
| char *hll_file; |
| char *end = NULL; |
| unsigned long val; |
| |
| cil_lexer_next(&tok); |
| if (tok.type != SYMBOL) { |
| cil_log(CIL_ERR, "Invalid line mark syntax\n"); |
| goto exit; |
| } |
| hll_type = cil_strpool_add(tok.value); |
| if (hll_type == CIL_KEY_HLL_LME) { |
| if (cil_stack_is_empty(stack)) { |
| cil_log(CIL_ERR, "Line mark end without start\n"); |
| goto exit; |
| } |
| pop_hll_info(stack, hll_lineno, hll_expand); |
| *current = (*current)->parent; |
| } else { |
| create_node(&node, *current, tok.line, *hll_lineno, NULL); |
| insert_node(node, *current); |
| *current = node; |
| |
| create_node(&node, *current, tok.line, *hll_lineno, CIL_KEY_SRC_INFO); |
| insert_node(node, *current); |
| |
| create_node(&node, *current, tok.line, *hll_lineno, CIL_KEY_SRC_HLL); |
| insert_node(node, *current); |
| |
| if (hll_type == CIL_KEY_HLL_LMS) { |
| *hll_expand = 0; |
| } else if (hll_type == CIL_KEY_HLL_LMX) { |
| *hll_expand = 1; |
| } else { |
| cil_log(CIL_ERR, "Invalid line mark syntax\n"); |
| goto exit; |
| } |
| |
| cil_lexer_next(&tok); |
| if (tok.type != SYMBOL) { |
| cil_log(CIL_ERR, "Invalid line mark syntax\n"); |
| goto exit; |
| } |
| |
| val = strtoul(tok.value, &end, 10); |
| if (errno == ERANGE || *end != '\0') { |
| cil_log(CIL_ERR, "Problem parsing line number for line mark\n"); |
| goto exit; |
| } |
| #if ULONG_MAX > UINT32_MAX |
| if (val > UINT32_MAX) { |
| cil_log(CIL_ERR, "Line mark line number > UINT32_MAX\n"); |
| goto exit; |
| } |
| #endif |
| *hll_lineno = val; |
| |
| push_hll_info(stack, *hll_lineno, *hll_expand); |
| |
| cil_lexer_next(&tok); |
| if (tok.type != SYMBOL && tok.type != QSTRING) { |
| cil_log(CIL_ERR, "Invalid line mark syntax\n"); |
| goto exit; |
| } |
| |
| if (tok.type == QSTRING) { |
| tok.value[strlen(tok.value) - 1] = '\0'; |
| tok.value = tok.value+1; |
| } |
| |
| hll_file = cil_strpool_add(tok.value); |
| |
| create_node(&node, *current, tok.line, *hll_lineno, hll_file); |
| insert_node(node, *current); |
| } |
| |
| cil_lexer_next(&tok); |
| if (tok.type != NEWLINE) { |
| cil_log(CIL_ERR, "Invalid line mark syntax\n"); |
| goto exit; |
| } |
| |
| return SEPOL_OK; |
| |
| exit: |
| cil_log(CIL_ERR, "Problem with high-level line mark at line %u of %s\n", tok.line, path); |
| return SEPOL_ERR; |
| } |
| |
| static void add_cil_path(struct cil_tree_node **current, char *path) |
| { |
| struct cil_tree_node *node; |
| |
| create_node(&node, *current, 0, 0, NULL); |
| insert_node(node, *current); |
| *current = node; |
| |
| create_node(&node, *current, 0, 0, CIL_KEY_SRC_INFO); |
| insert_node(node, *current); |
| |
| create_node(&node, *current, 0, 0, CIL_KEY_SRC_CIL); |
| insert_node(node, *current); |
| |
| create_node(&node, *current, 0, 0, path); |
| insert_node(node, *current); |
| } |
| |
| int cil_parser(const char *_path, char *buffer, uint32_t size, struct cil_tree **parse_tree) |
| { |
| |
| int paren_count = 0; |
| |
| struct cil_tree *tree = NULL; |
| struct cil_tree_node *node = NULL; |
| struct cil_tree_node *current = NULL; |
| char *path = cil_strpool_add(_path); |
| struct cil_stack *stack; |
| uint32_t hll_lineno = 0; |
| uint32_t hll_expand = 0; |
| struct token tok; |
| int rc = SEPOL_OK; |
| |
| CIL_KEY_HLL_LMS = cil_strpool_add("lms"); |
| CIL_KEY_HLL_LMX = cil_strpool_add("lmx"); |
| CIL_KEY_HLL_LME = cil_strpool_add("lme"); |
| |
| cil_stack_init(&stack); |
| |
| cil_lexer_setup(buffer, size); |
| |
| tree = *parse_tree; |
| current = tree->root; |
| |
| add_cil_path(¤t, path); |
| |
| do { |
| cil_lexer_next(&tok); |
| switch (tok.type) { |
| case HLL_LINEMARK: |
| rc = add_hll_linemark(¤t, &hll_lineno, &hll_expand, stack, path); |
| if (rc != SEPOL_OK) { |
| goto exit; |
| } |
| break; |
| case OPAREN: |
| paren_count++; |
| if (paren_count > CIL_PARSER_MAX_EXPR_DEPTH) { |
| cil_log(CIL_ERR, "Number of open parenthesis exceeds limit of %d at line %d of %s\n", CIL_PARSER_MAX_EXPR_DEPTH, tok.line, path); |
| goto exit; |
| } |
| create_node(&node, current, tok.line, hll_lineno, NULL); |
| insert_node(node, current); |
| current = node; |
| break; |
| case CPAREN: |
| paren_count--; |
| if (paren_count < 0) { |
| cil_log(CIL_ERR, "Close parenthesis without matching open at line %d of %s\n", tok.line, path); |
| goto exit; |
| } |
| current = current->parent; |
| break; |
| case QSTRING: |
| tok.value[strlen(tok.value) - 1] = '\0'; |
| tok.value = tok.value+1; |
| /* FALLTHRU */ |
| case SYMBOL: |
| if (paren_count == 0) { |
| cil_log(CIL_ERR, "Symbol not inside parenthesis at line %d of %s\n", tok.line, path); |
| goto exit; |
| } |
| |
| create_node(&node, current, tok.line, hll_lineno, cil_strpool_add(tok.value)); |
| insert_node(node, current); |
| break; |
| case NEWLINE : |
| if (!hll_expand) { |
| hll_lineno++; |
| } |
| break; |
| case COMMENT: |
| while (tok.type != NEWLINE && tok.type != END_OF_FILE) { |
| cil_lexer_next(&tok); |
| } |
| if (!hll_expand) { |
| hll_lineno++; |
| } |
| if (tok.type != END_OF_FILE) { |
| break; |
| } |
| /* FALLTHRU */ |
| // Fall through if EOF |
| case END_OF_FILE: |
| if (paren_count > 0) { |
| cil_log(CIL_ERR, "Open parenthesis without matching close at line %d of %s\n", tok.line, path); |
| goto exit; |
| } |
| if (!cil_stack_is_empty(stack)) { |
| cil_log(CIL_ERR, "High-level language line marker start without close at line %d of %s\n", tok.line, path); |
| goto exit; |
| } |
| break; |
| case UNKNOWN: |
| cil_log(CIL_ERR, "Invalid token '%s' at line %d of %s\n", tok.value, tok.line, path); |
| goto exit; |
| default: |
| cil_log(CIL_ERR, "Unknown token type '%d' at line %d of %s\n", tok.type, tok.line, path); |
| goto exit; |
| } |
| } |
| while (tok.type != END_OF_FILE); |
| |
| cil_lexer_destroy(); |
| |
| cil_stack_destroy(&stack); |
| |
| *parse_tree = tree; |
| |
| return SEPOL_OK; |
| |
| exit: |
| while (!cil_stack_is_empty(stack)) { |
| pop_hll_info(stack, &hll_lineno, &hll_expand); |
| } |
| cil_lexer_destroy(); |
| cil_stack_destroy(&stack); |
| |
| return SEPOL_ERR; |
| } |