blob: 2852f5eff3e8ab3ea898c2e9def623bb01fbcacb [file]
#!/bin/env python
# -*- coding: utf-8 -*-
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import re
import os, glob
p_any = '(?:\S| |\t|\n+\t|\n+ |\n+#|\n+/|\n+\$)+?'
p_spaces = '\s*'
def create_pattern(funcCall):
return '\n(\w+)\s+?(\w+)' + p_spaces + '\((' + p_any + ')\)' + p_spaces + '(\n\{' + '(' + p_any + ')' + f'({funcCall})' + '\s*' + '(' + p_any + ')' + '\n+\})'
pattern = create_pattern('ereturn')
source_filenames = ['time.c', 'date.c', 'timedate.c', 'int.c', 'float.c', 'bool.c', 'char.c', 'formatting.c', 'json.c', 'jsonb.c', 'nabstime.c', 'numeric.c', 'timestamp.c', 'network.c']
# verify all convert and in&outs(if in converts) are 'soft'-error handling
# safe error handle == sub-calls are not 'ereport' (replaced by 'ereturn')
boolin = '''
const char *in_str = PG_GETARG_CSTRING(0);
const char *str;
size_t len;
bool result;
/*
* Skip leading and trailing whitespace
*/
str = in_str;
while (isspace((unsigned char) *str))
str++;
len = strlen(str);
while (len > 0 && isspace((unsigned char) str[len - 1]))
len--;
if (parse_bool_with_len(str, len, &result))
PG_RETURN_BOOL(result);
ereturn(fcinfo->context, 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type boolean: \"%s\"", in_str)));
/* not reached */
PG_RETURN_BOOL(false);
'''
def find_functions_with_call(functions):
ddd = '|'.join(functions + ['ereturn'])
call_pattern = f'(?:{ddd})'
function_with_call_pattern = create_pattern(call_pattern)
caller_functions = []
caller_pg_functions = []
unadapted_functions = {}
unadapted_calls = []
functions_with_unadapted_calls = {}
for root, subdirs, files in os.walk('../..'):
for filename in files:
file_path = os.path.join(root, filename)
# if filename[-2:] == '.c':
if filename in source_filenames:
# print(file_path)
with open(file_path, 'r') as f:
content = f.read()
# content = 'lol () { print(\'sds\') ereturn(wwe); }'
matches = re.findall(function_with_call_pattern, content)
if len(matches) > 0:
for m in matches:
# print('!!!!!!!!!!', file_path, m[0], m[1], m[2], m[3], '$$$$$$$$', m[4])
caller_functions += [m[1]]
if m[2] == 'PG_FUNCTION_ARGS':
caller_pg_functions += [m[1]]
if (m[0] != 'bool' or re.search('escontext', m[2]) is None) and m[2] != 'PG_FUNCTION_ARGS':
unadapted_functions[m[1]] = True
if m[5] != 'ereturn':
func_call = m[4][-20:] + m[5] + m[6][:20]
safe_call_pattern = f'if{p_spaces}\(!{m[5]}\({p_any}\)\)'
safe_call_void_pattern = f'\(void\){p_spaces}{m[5]}\({p_any}\)\)'
direct_safe_call_pattern = f'DirectFunctionCall1Safe\({m[5]}'
if re.search(safe_call_pattern, func_call) is None and \
re.search(safe_call_void_pattern, func_call) is None and \
re.search(direct_safe_call_pattern, func_call) is None:
unadapted_calls += [m[5]]
if m[1] in functions_with_unadapted_calls:
functions_with_unadapted_calls[m[1]] += [m[5]]
else:
functions_with_unadapted_calls[m[1]] = [m[5]]
return caller_functions, caller_pg_functions, unadapted_functions, unadapted_calls, functions_with_unadapted_calls
caller_functions = []
# print(re.findall('!' + p_any + '\n*!', f'!{boolin}!'))
while True:
new_caller_functions, new_caller_pg_functions, unadapted_functions, unadapted_calls, functions_with_unadapted_calls = find_functions_with_call(caller_functions)
if len(caller_functions) == len(new_caller_functions):
break
# for c in sorted(new_caller_functions):
# if c not in caller_functions:
# print(c)
for f in unadapted_functions:
print(f)
caller_functions = new_caller_functions
print(len(caller_functions), len(unadapted_functions), len(unadapted_calls), len(functions_with_unadapted_calls))
# replase res = func(intput) -> if (!func(input, &res, escontext)) return false;
# define safe_call(functionCall, input...) if (!functionCall(input)) return false;
for f in sorted(functions_with_unadapted_calls):
print(f, functions_with_unadapted_calls[f])