Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Win32 micropython builds corrupt memory upon repeated execution of a particular function #4652

Closed
@ddiminnie

Description

@ddiminnie

(I apologize in advance for not finding a simpler example/use case for this.)
While running tests on our own fork of micropython, we encountered a bug that crashed the python interpreter (on Windows 32-bit builds only). This bug is also present in the 'stock' 1.10 release of Micropython. Rather than upload our entire test setup, the following (somewhat simpler) example should serve to illustrate the problem. Using the python module listed at the end of this comment, try the following (may need to repeat the last instruction a few times to see the issue):

MicroPython v1.10 on 2019-03-28; win32 version
Use Ctrl-D to exit, Ctrl-E for paste mode
>>> from sys import path
>>> path.append(r'<path where flt_hex.py is stored>')
>>> from flt_hex import flt_hex
>>> for idx in range(20000):  exec('flt_hex(float({:d}))'.format(idx), locals(), globals())

After one or more iterations of the final step, the following assertion is triggered (followed by shutdown of the micropython.exe process):

>>> for idx in range(20000):  exec('flt_hex(float({:d}))'.format(idx), locals(), globals())
Assertion failed: ATB_GET_KIND(block) == AT_HEAD, file <redacted>\micropython\py\gc.c, line 587

Listing for file 'flt_hex.py':

from array import array
from math import isinf, isnan
from sys import byteorder

_IS_DOUBLE = (1e-100 > 0)


def flt_hex(flt):
    """
    Mimics the behavior of the 'hex()' float instance method, for platforms where this method 
    is not implemented.

    :param flt: floating-point value to be converted.
    :return: hexadecimal string representation of flt.
    """
    if not isinstance(flt, float):
        raise TypeError('first argument must be of type "float"')
    
    if isnan(flt) or isinf(flt):
        result = str(flt)
    else:
        # Form the string 
        #   s0xc.mm...pe
        # where 
        # s(ign) = '-' if flt is negative else '',
        # c(haracteristic) = 1 if flt is normalized else 0,
        # each m represent one digit of the fractional part of the significand (the 'mantissa')
        # e(xponent) is the power of 2

        # Convert to a list of integers (bytes objects are not trivially reversible in 
        # MicroPython)
        bv = list(bytes(array('d' if _IS_DOUBLE else 'f', [flt])))
        if byteorder == 'little':
            bv = bv[::-1]
        
        bv_len = len(bv)  # 8 for double; 4 for single

        # From IEEE-754 (1985), float layouts (big endian) are
        # 0bseee eeee efff ffff ffff ffff ffff ffff  for single precision
        # 0bseee eeee eeee ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff for 
        # double precision
        s = '-' if (bv[0] & 0x80) else ''
        
        ee = ((((bv[0] & 0x7F) << 4) + ((bv[1] & 0xF0) >> 4)) if _IS_DOUBLE 
              else (((bv[0] & 0x7F) << 1) + ((bv[1] & 0x80) >> 7)))
        ff = (bv[1] & (0x0F if _IS_DOUBLE else 0x7F)) << (8*(bv_len - 2))
        ff += sum((val << (8*(bv_len - 3 - idx))) for idx, val in enumerate(bv[2:]))
        
        if ee == 0:
            # Zero or denormalized
            characteristic = '0'
            if ff:
                exponent = '-1022' if _IS_DOUBLE else '-126'
                # Since there are 23 bits after the decimal point for single precision, we 
                # need to shift left by one bit to fit in hex format (the last bit in the 
                # output should be ignored)
                m = '{:=013x}'.format(ff) if _IS_DOUBLE else '{:=06x}'.format(ff << 1)
            else:
                exponent = '+0'
                m = '0'
        else:
            # Normalized floats
            characteristic = '1'
            exponent = '{:+d}'.format(ee - (1023 if _IS_DOUBLE else 127))
            m = '{:=013x}'.format(ff) if _IS_DOUBLE else '{:=06x}'.format(ff << 1)
                    
        result = '{s}0x{characteristic}.{m}p{exponent}'.format(s=s, 
                                                               characteristic=characteristic, 
                                                               m=m,
                                                               exponent=exponent)
            
    return result

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions