From d316eefeb4b857238f2e9f15322239b00e757277 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 5 Mar 2018 09:03:42 -0800 Subject: [PATCH 1/2] bpo-33001: Minimal fix to prevent buffer overrun in os.symlink --- Lib/test/test_os.py | 42 +++++++++ .../2018-03-05-10-09-51.bpo-33001.elj4Aa.rst | 1 + Modules/posixmodule.c | 94 +++++++++++-------- 3 files changed, 99 insertions(+), 38 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2018-03-05-10-09-51.bpo-33001.elj4Aa.rst diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index bb5d2e3429ce0f..e000b80f5e1b8a 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2117,6 +2117,48 @@ def test_12084(self): os.remove(file1) shutil.rmtree(level1) + def test_buffer_overflow(self): + # Older versions would have a buffer overflow when detecting + # whether a link source was a directory. This test ensures we + # no longer crash, but does not otherwise validate the behavior + segment = 'X' * 27 + path = os.path.join(*[segment] * 10) + test_cases = [ + # overflow with absolute src + ('\\' + path, segment), + # overflow dest with relative src + (segment, path), + # overflow dest when appending '\\' for join + (segment, path[:261]), + # overflow when joining src + (path[:180], path[:180]), + ] + for src, dest in test_cases: + try: + os.symlink(src, dest) + except FileNotFoundError: + pass + else: + try: + os.remove(dest) + except OSError: + pass + # Also test with bytes, since that is a separate code path. + # However, because of the stack layout, it is not possible + # to exploit the overflow on Python 3.5 using bytes + try: + os.symlink(os.fsencode(src), os.fsencode(dest)) + except ValueError: + # Conversion function checks for len(arg) >= 260 + pass + except FileNotFoundError: + pass + else: + try: + os.remove(dest) + except OSError: + pass + @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") class Win32JunctionTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Security/2018-03-05-10-09-51.bpo-33001.elj4Aa.rst b/Misc/NEWS.d/next/Security/2018-03-05-10-09-51.bpo-33001.elj4Aa.rst new file mode 100644 index 00000000000000..2acbac9e1af6f8 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2018-03-05-10-09-51.bpo-33001.elj4Aa.rst @@ -0,0 +1 @@ +Minimal fix to prevent buffer overrun in os.symlink on Windows diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index d42416b0270ebb..102c5405bd5972 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7259,39 +7259,50 @@ check_CreateSymbolicLink(void) return (Py_CreateSymbolicLinkW && Py_CreateSymbolicLinkA); } -/* Remove the last portion of the path */ -static void +/* Remove the last portion of the path - return 0 on success */ +static int _dirnameW(WCHAR *path) { WCHAR *ptr; + size_t length = wcsnlen_s(path, MAX_PATH); + if (length == MAX_PATH) { + return -1; + } /* walk the path from the end until a backslash is encountered */ - for(ptr = path + wcslen(path); ptr != path; ptr--) { + for(ptr = path + length; ptr != path; ptr--) { if (*ptr == L'\\' || *ptr == L'/') break; } *ptr = 0; + return 0; } -/* Remove the last portion of the path */ -static void +/* Remove the last portion of the path - return 0 on success */ +static int _dirnameA(char *path) { char *ptr; + size_t length = strnlen_s(path, MAX_PATH); + if (length == MAX_PATH) { + return -1; + } /* walk the path from the end until a backslash is encountered */ - for(ptr = path + strlen(path); ptr != path; ptr--) { + for(ptr = path + length; ptr != path; ptr--) { if (*ptr == '\\' || *ptr == '/') break; } *ptr = 0; + return 0; } /* Is this path absolute? */ static int _is_absW(const WCHAR *path) { - return path[0] == L'\\' || path[0] == L'/' || path[1] == L':'; + return path[0] == L'\\' || path[0] == L'/' || + (path[0] && path[1] == L':'); } @@ -7299,50 +7310,47 @@ _is_absW(const WCHAR *path) static int _is_absA(const char *path) { - return path[0] == '\\' || path[0] == '/' || path[1] == ':'; + return path[0] == '\\' || path[0] == '/' || + (path[0] && path[1] == ':'); } -/* join root and rest with a backslash */ -static void +/* join root and rest with a backslash - return 0 on success */ +static int _joinW(WCHAR *dest_path, const WCHAR *root, const WCHAR *rest) { - size_t root_len; - if (_is_absW(rest)) { - wcscpy(dest_path, rest); - return; + return wcscpy_s(dest_path, MAX_PATH, rest); } - root_len = wcslen(root); + if (wcscpy_s(dest_path, MAX_PATH, root)) { + return -1; + } - wcscpy(dest_path, root); - if(root_len) { - dest_path[root_len] = L'\\'; - root_len++; + if (dest_path[0] && wcscat_s(dest_path, MAX_PATH, L"\\")) { + return -1; } - wcscpy(dest_path+root_len, rest); + + return wcscat_s(dest_path, MAX_PATH, rest); } -/* join root and rest with a backslash */ -static void +/* join root and rest with a backslash - return 0 on success */ +static int _joinA(char *dest_path, const char *root, const char *rest) { - size_t root_len; - if (_is_absA(rest)) { - strcpy(dest_path, rest); - return; + return strcpy_s(dest_path, MAX_PATH, rest); } - root_len = strlen(root); + if (strcpy_s(dest_path, MAX_PATH, root)) { + return -1; + } - strcpy(dest_path, root); - if(root_len) { - dest_path[root_len] = '\\'; - root_len++; + if (dest_path[0] && strcat_s(dest_path, MAX_PATH, "\\")) { + return -1; } - strcpy(dest_path+root_len, rest); + + return strcat_s(dest_path, MAX_PATH, rest); } /* Return True if the path at src relative to dest is a directory */ @@ -7354,10 +7362,14 @@ _check_dirW(WCHAR *src, WCHAR *dest) WCHAR src_resolved[MAX_PATH] = L""; /* dest_parent = os.path.dirname(dest) */ - wcscpy(dest_parent, dest); - _dirnameW(dest_parent); + if (wcscpy_s(dest_parent, MAX_PATH, dest) || + _dirnameW(dest_parent)) { + return 0; + } /* src_resolved = os.path.join(dest_parent, src) */ - _joinW(src_resolved, dest_parent, src); + if (_joinW(src_resolved, dest_parent, src)) { + return 0; + } return ( GetFileAttributesExW(src_resolved, GetFileExInfoStandard, &src_info) && src_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY @@ -7373,10 +7385,14 @@ _check_dirA(char *src, char *dest) char src_resolved[MAX_PATH] = ""; /* dest_parent = os.path.dirname(dest) */ - strcpy(dest_parent, dest); - _dirnameA(dest_parent); + if (strcpy_s(dest_parent, MAX_PATH, dest) || + _dirnameA(dest_parent)) { + return 0; + } /* src_resolved = os.path.join(dest_parent, src) */ - _joinA(src_resolved, dest_parent, src); + if (_joinA(src_resolved, dest_parent, src)) { + return 0; + } return ( GetFileAttributesExA(src_resolved, GetFileExInfoStandard, &src_info) && src_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY @@ -7441,6 +7457,7 @@ os_symlink_impl(PyObject *module, path_t *src, path_t *dst, #ifdef MS_WINDOWS Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH if (dst->wide) { /* if src is a directory, ensure target_is_directory==1 */ target_is_directory |= _check_dirW(src->wide, dst->wide); @@ -7453,6 +7470,7 @@ os_symlink_impl(PyObject *module, path_t *src, path_t *dst, result = Py_CreateSymbolicLinkA(dst->narrow, src->narrow, target_is_directory); } + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS if (!result) From ac732bec4d227a38ebb57a83a6caef0df671a219 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 5 Mar 2018 13:23:22 -0800 Subject: [PATCH 2/2] Remove invalid test --- Lib/test/test_os.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index e000b80f5e1b8a..e2077383bf4784 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -2128,8 +2128,6 @@ def test_buffer_overflow(self): ('\\' + path, segment), # overflow dest with relative src (segment, path), - # overflow dest when appending '\\' for join - (segment, path[:261]), # overflow when joining src (path[:180], path[:180]), ] pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy