Author: Maciej Fijalkowski <fij...@gmail.com> Branch: Changeset: r73535:06bb85dd0910 Date: 2014-09-14 15:54 -0600 http://bitbucket.org/pypy/pypy/changeset/06bb85dd0910/
Log: merge diff --git a/pypy/doc/windows.rst b/pypy/doc/windows.rst --- a/pypy/doc/windows.rst +++ b/pypy/doc/windows.rst @@ -85,10 +85,13 @@ Abridged method (for -Ojit builds using Visual Studio 2008) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Download the versions of all the external packages -from +Download the versions of all the external packages from +https://bitbucket.org/pypy/pypy/downloads/local_2.4.zip +(for 2.4 release and later) or https://bitbucket.org/pypy/pypy/downloads/local.zip -Then expand it into the base directory (base_dir) and modify your environment to reflect this:: +(for pre-2.4 versions) +Then expand it into the base directory (base_dir) and modify your environment +to reflect this:: set PATH=<base_dir>\bin;<base_dir>\tcltk\bin;%PATH% set INCLUDE=<base_dir>\include;<base_dir>\tcltk\include;%INCLUDE% diff --git a/pypy/interpreter/pycode.py b/pypy/interpreter/pycode.py --- a/pypy/interpreter/pycode.py +++ b/pypy/interpreter/pycode.py @@ -38,18 +38,15 @@ def cpython_code_signature(code): "([list-of-arg-names], vararg-name-or-None, kwarg-name-or-None)." argcount = code.co_argcount + varnames = code.co_varnames assert argcount >= 0 # annotator hint - argnames = list(code.co_varnames[:argcount]) + argnames = list(varnames[:argcount]) if code.co_flags & CO_VARARGS: - varargname = code.co_varnames[argcount] + varargname = varnames[argcount] argcount += 1 else: varargname = None - if code.co_flags & CO_VARKEYWORDS: - kwargname = code.co_varnames[argcount] - argcount += 1 - else: - kwargname = None + kwargname = varnames[argcount] if code.co_flags & CO_VARKEYWORDS else None return Signature(argnames, varargname, kwargname) diff --git a/pypy/interpreter/test/test_app_main.py b/pypy/interpreter/test/test_app_main.py --- a/pypy/interpreter/test/test_app_main.py +++ b/pypy/interpreter/test/test_app_main.py @@ -945,7 +945,7 @@ prefix = udir.join('pathtest').ensure(dir=1) fake_exe = 'bin/pypy-c' if sys.platform == 'win32': - fake_exe += '.exe' + fake_exe = 'pypy-c.exe' fake_exe = prefix.join(fake_exe).ensure(file=1) expected_path = [str(prefix.join(subdir).ensure(dir=1)) for subdir in ('lib_pypy', @@ -985,8 +985,10 @@ assert sys.path == old_sys_path + [self.goal_dir] app_main.setup_bootstrap_path(self.fake_exe) - assert sys.executable == '' # not executable! - assert sys.path == old_sys_path + [self.goal_dir] + if not sys.platform == 'win32': + # an existing file is always 'executable' on windows + assert sys.executable == '' # not executable! + assert sys.path == old_sys_path + [self.goal_dir] os.chmod(self.fake_exe, 0755) app_main.setup_bootstrap_path(self.fake_exe) diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py --- a/pypy/module/_ssl/interp_ssl.py +++ b/pypy/module/_ssl/interp_ssl.py @@ -775,7 +775,7 @@ else: r, w, e = rpoll.select([sock_fd], [], [], sock_timeout) ready = r - except SelectError, e: + except rpoll.SelectError as e: message = e.get_msg() raise ssl_error(space, message, e.errno) if ready: diff --git a/pypy/module/operator/__init__.py b/pypy/module/operator/__init__.py --- a/pypy/module/operator/__init__.py +++ b/pypy/module/operator/__init__.py @@ -39,7 +39,7 @@ 'irshift', 'isub', 'itruediv', 'ixor', '_length_hint'] interpleveldefs = { - '_compare_digest': 'interp_operator.compare_digest', + '_compare_digest': 'tscmp.compare_digest', } for name in interp_names: diff --git a/pypy/module/operator/interp_operator.py b/pypy/module/operator/interp_operator.py --- a/pypy/module/operator/interp_operator.py +++ b/pypy/module/operator/interp_operator.py @@ -1,6 +1,4 @@ -from rpython.rlib.objectmodel import specialize - -from pypy.interpreter.error import OperationError, oefmt +from pypy.interpreter.error import OperationError from pypy.interpreter.gateway import unwrap_spec @@ -249,33 +247,3 @@ @unwrap_spec(default=int) def _length_hint(space, w_iterable, default): return space.wrap(space.length_hint(w_iterable, default)) - -def compare_digest(space, w_a, w_b): - if ( - space.isinstance_w(w_a, space.w_unicode) and - space.isinstance_w(w_b, space.w_unicode) - ): - return space.wrap(tscmp(space.unicode_w(w_a), space.unicode_w(w_b))) - if ( - space.isinstance_w(w_a, space.w_unicode) or - space.isinstance_w(w_b, space.w_unicode) - ): - raise oefmt( - space.w_TypeError, - "unsupported operand types(s) or combination of types: '%N' and '%N'", - w_a, - w_b, - ) - else: - return space.wrap(tscmp(space.bufferstr_w(w_a), space.bufferstr_w(w_b))) - - -@specialize.argtype(0, 1) -def tscmp(a, b): - len_a = len(a) - len_b = len(b) - length = min(len(a), len(b)) - res = len_a ^ len_b - for i in xrange(length): - res |= ord(a[i]) ^ ord(b[i]) - return res == 0 diff --git a/pypy/module/operator/test/test_operator.py b/pypy/module/operator/test/test_operator.py --- a/pypy/module/operator/test/test_operator.py +++ b/pypy/module/operator/test/test_operator.py @@ -334,3 +334,9 @@ assert operator._compare_digest(a, b) a, b = mybytes(b"foobar"), mybytes(b"foobaz") assert not operator._compare_digest(a, b) + + def test_compare_digest_unicode(self): + import operator + assert operator._compare_digest(u'asd', u'asd') + assert not operator._compare_digest(u'asd', u'qwe') + raises(TypeError, operator._compare_digest, u'asd', b'qwe') diff --git a/pypy/module/operator/test/test_tscmp.py b/pypy/module/operator/test/test_tscmp.py new file mode 100644 --- /dev/null +++ b/pypy/module/operator/test/test_tscmp.py @@ -0,0 +1,28 @@ +from pypy.module.operator.tscmp import pypy_tscmp, pypy_tscmp_wide + +class TestTimingSafeCompare: + tostr = str + tscmp = staticmethod(pypy_tscmp) + + def test_tscmp_neq(self): + assert not self.tscmp(self.tostr('asd'), self.tostr('qwe'), 3, 3) + + def test_tscmp_eq(self): + assert self.tscmp(self.tostr('asd'), self.tostr('asd'), 3, 3) + + def test_tscmp_len(self): + assert self.tscmp(self.tostr('asdp'), self.tostr('asdq'), 3, 3) + + def test_tscmp_nlen(self): + assert not self.tscmp(self.tostr('asd'), self.tostr('asd'), 2, 3) + + +class TestTimingSafeCompareWide(TestTimingSafeCompare): + tostr = unicode + tscmp = staticmethod(pypy_tscmp_wide) + + def test_tscmp_wide_nonascii(self): + a, b = u"\ud808\udf45", u"\ud808\udf45" + assert self.tscmp(a, b, len(a), len(b)) + a, b = u"\ud808\udf45", u"\ud808\udf45 " + assert not self.tscmp(a, b, len(a), len(b)) diff --git a/pypy/module/operator/tscmp.c b/pypy/module/operator/tscmp.c new file mode 100644 --- /dev/null +++ b/pypy/module/operator/tscmp.c @@ -0,0 +1,80 @@ +/* Derived from CPython 3.3.5's operator.c::_tscmp + */ + +#include <stdlib.h> +#include <wchar.h> +#include "tscmp.h" + +int +pypy_tscmp(const char *a, const char *b, long len_a, long len_b) +{ + /* The volatile type declarations make sure that the compiler has no + * chance to optimize and fold the code in any way that may change + * the timing. + */ + volatile long length; + volatile const char *left; + volatile const char *right; + long i; + char result; + + /* loop count depends on length of b */ + length = len_b; + left = NULL; + right = b; + + /* don't use else here to keep the amount of CPU instructions constant, + * volatile forces re-evaluation + * */ + if (len_a == length) { + left = *((volatile const char**)&a); + result = 0; + } + if (len_a != length) { + left = b; + result = 1; + } + + for (i=0; i < length; i++) { + result |= *left++ ^ *right++; + } + + return (result == 0); +} + +int +pypy_tscmp_wide(const wchar_t *a, const wchar_t *b, long len_a, long len_b) +{ + /* The volatile type declarations make sure that the compiler has no + * chance to optimize and fold the code in any way that may change + * the timing. + */ + volatile long length; + volatile const wchar_t *left; + volatile const wchar_t *right; + long i; + wchar_t result; + + /* loop count depends on length of b */ + length = len_b; + left = NULL; + right = b; + + /* don't use else here to keep the amount of CPU instructions constant, + * volatile forces re-evaluation + * */ + if (len_a == length) { + left = *((volatile const wchar_t**)&a); + result = 0; + } + if (len_a != length) { + left = b; + result = 1; + } + + for (i=0; i < length; i++) { + result |= *left++ ^ *right++; + } + + return (result == 0); +} diff --git a/pypy/module/operator/tscmp.h b/pypy/module/operator/tscmp.h new file mode 100644 --- /dev/null +++ b/pypy/module/operator/tscmp.h @@ -0,0 +1,2 @@ +int pypy_tscmp(const char *, const char *, long, long); +int pypy_tscmp_wide(const wchar_t *, const wchar_t *, long, long); diff --git a/pypy/module/operator/tscmp.py b/pypy/module/operator/tscmp.py new file mode 100644 --- /dev/null +++ b/pypy/module/operator/tscmp.py @@ -0,0 +1,73 @@ +""" +Provides _compare_digest method, which is a safe comparing to prevent timing +attacks for the hmac module. +""" +import py + +from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.translator.tool.cbuild import ExternalCompilationInfo + +from pypy.interpreter.error import oefmt + +cwd = py.path.local(__file__).dirpath() +eci = ExternalCompilationInfo( + includes=[cwd.join('tscmp.h')], + include_dirs=[str(cwd)], + separate_module_files=[cwd.join('tscmp.c')], + export_symbols=['pypy_tscmp', 'pypy_tscmp_wide']) + + +def llexternal(*args, **kwargs): + kwargs.setdefault('compilation_info', eci) + kwargs.setdefault('sandboxsafe', True) + return rffi.llexternal(*args, **kwargs) + + +pypy_tscmp = llexternal( + 'pypy_tscmp', + [rffi.CCHARP, rffi.CCHARP, rffi.LONG, rffi.LONG], + rffi.INT) +pypy_tscmp_wide = llexternal( + 'pypy_tscmp_wide', + [rffi.CWCHARP, rffi.CWCHARP, rffi.LONG, rffi.LONG], + rffi.INT) + + +def compare_digest(space, w_a, w_b): + """compare_digest(a, b) -> bool + + Return 'a == b'. This function uses an approach designed to prevent + timing analysis, making it appropriate for cryptography. a and b + must both be of the same type: either str (ASCII only), or any type + that supports the buffer protocol (e.g. bytes). + + Note: If a and b are of different lengths, or if an error occurs, a + timing attack could theoretically reveal information about the types + and lengths of a and b--but not their values. + """ + if (space.isinstance_w(w_a, space.w_unicode) and + space.isinstance_w(w_b, space.w_unicode)): + a = space.unicode_w(w_a) + b = space.unicode_w(w_b) + with rffi.scoped_nonmoving_unicodebuffer(a) as a_buf: + with rffi.scoped_nonmoving_unicodebuffer(b) as b_buf: + result = pypy_tscmp_wide(a_buf, b_buf, len(a), len(b)) + return space.wrap(rffi.cast(lltype.Bool, result)) + return compare_digest_buffer(space, w_a, w_b) + + +def compare_digest_buffer(space, w_a, w_b): + try: + a_buf = w_a.buffer_w(space, space.BUF_SIMPLE) + b_buf = w_b.buffer_w(space, space.BUF_SIMPLE) + except TypeError: + raise oefmt(space.w_TypeError, + "unsupported operand types(s) or combination of types: " + "'%T' and '%T'", w_a, w_b) + + a = a_buf.as_str() + b = b_buf.as_str() + with rffi.scoped_nonmovingbuffer(a) as a_buf: + with rffi.scoped_nonmovingbuffer(b) as b_buf: + result = pypy_tscmp(a_buf, b_buf, len(a), len(b)) + return space.wrap(rffi.cast(lltype.Bool, result)) diff --git a/rpython/translator/platform/test/test_makefile.py b/rpython/translator/platform/test/test_makefile.py --- a/rpython/translator/platform/test/test_makefile.py +++ b/rpython/translator/platform/test/test_makefile.py @@ -44,6 +44,7 @@ assert res.returncode == 0 def test_900_files(self): + tmpdir = udir.join('test_900_files').ensure(dir=1) txt = '#include <stdio.h>\n' for i in range(900): txt += 'int func%03d();\n' % i @@ -52,11 +53,11 @@ txt += ' j += func%03d();\n' % i txt += ' printf("%d\\n", j);\n' txt += ' return 0;};\n' - cfile = udir.join('test_900_files.c') + cfile = tmpdir.join('test_900_files.c') cfile.write(txt) cfiles = [cfile] for i in range(900): - cfile2 = udir.join('implement%03d.c' %i) + cfile2 = tmpdir.join('implement%03d.c' %i) cfile2.write(''' int func%03d() { @@ -64,10 +65,10 @@ } ''' % (i, i)) cfiles.append(cfile2) - mk = self.platform.gen_makefile(cfiles, ExternalCompilationInfo(), path=udir) + mk = self.platform.gen_makefile(cfiles, ExternalCompilationInfo(), path=tmpdir) mk.write() self.platform.execute_makefile(mk) - res = self.platform.execute(udir.join('test_900_files')) + res = self.platform.execute(tmpdir.join('test_900_files')) self.check_res(res, '%d\n' %sum(range(900))) def test_precompiled_headers(self): diff --git a/rpython/translator/platform/windows.py b/rpython/translator/platform/windows.py --- a/rpython/translator/platform/windows.py +++ b/rpython/translator/platform/windows.py @@ -203,6 +203,9 @@ # the assembler still has the old behavior that all options # must come first, and after the file name all options are ignored. # So please be careful with the order of parameters! ;-) + pdb_dir = oname.dirname + if pdb_dir: + compile_args += ['/Fd%s\\' % (pdb_dir,)] args = ['/nologo', '/c'] + compile_args + ['/Fo%s' % (oname,), str(cfile)] self._execute_c_compiler(cc, args, oname) return oname diff --git a/rpython/translator/sandbox/rsandbox.py b/rpython/translator/sandbox/rsandbox.py --- a/rpython/translator/sandbox/rsandbox.py +++ b/rpython/translator/sandbox/rsandbox.py @@ -60,8 +60,7 @@ def need_more_data(self): buflen = self.buflen - buf = lltype.malloc(rffi.CCHARP.TO, buflen, flavor='raw') - try: + with lltype.scoped_alloc(rffi.CCHARP.TO, buflen) as buf: buflen = rffi.cast(rffi.SIZE_T, buflen) count = ll_read_not_sandboxed(self.fd, buf, buflen) count = rffi.cast(lltype.Signed, count) @@ -69,20 +68,15 @@ raise IOError self.buf += ''.join([buf[i] for i in range(count)]) self.buflen *= 2 - finally: - lltype.free(buf, flavor='raw') def sandboxed_io(buf): STDIN = 0 STDOUT = 1 # send the buffer with the marshalled fnname and input arguments to STDOUT - p = lltype.malloc(rffi.CCHARP.TO, len(buf), flavor='raw') - try: + with lltype.scoped_alloc(rffi.CCHARP.TO, len(buf)) as p: for i in range(len(buf)): p[i] = buf[i] writeall_not_sandboxed(STDOUT, p, len(buf)) - finally: - lltype.free(p, flavor='raw') # build a Loader that will get the answer from STDIN loader = FdLoader(STDIN) # check for errors @@ -108,9 +102,8 @@ @signature(types.str(), returns=types.impossible()) def not_implemented_stub(msg): STDERR = 2 - buf = rffi.str2charp(msg + '\n') - writeall_not_sandboxed(STDERR, buf, len(msg) + 1) - rffi.free_charp(buf) + with rffi.scoped_str2charp(msg + '\n') as buf: + writeall_not_sandboxed(STDERR, buf, len(msg) + 1) raise RuntimeError(msg) # XXX in RPython, the msg is ignored at the moment dump_string = rmarshal.get_marshaller(str) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit