This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Using __slots__ with str derived classes can cause segfault
Type: Stage:
Components: None Versions:
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: jhylton Nosy List: gvanrossum, jhylton, jpe, nnorwitz
Priority: high Keywords: patch

Created on 2003-06-20 16:28 by jpe, last changed 2022-04-10 16:09 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
slots.diff nnorwitz, 2003-06-20 21:23 fix crash w/slots 1
slots.diff nnorwitz, 2003-06-20 22:05 patch 2
Messages (7)
msg44085 - (view) Author: John Ehresman (jpe) * Date: 2003-06-20 16:28
The following results in a segfault on Linux with 2.3b1:
-------------------------
class StringDerived(str):
  __slots__ = ['a']
  def __init__(self, string):
    str.__init__(self, string)
    self.a = 1

class DerivedAgain(StringDerived):
  pass

o = DerivedAgain('hello world')
o.b = 2
--------------------------
Python 2.2.2 raises a TypeError when attempting to
derive a class from str with a non-empty __slots__,
maybe 2.3 should do the same?

I have no use case for deriving from str and defining
__slots__; I encountered the bug when writing test
cases for a debugger.
msg44086 - (view) Author: Neal Norwitz (nnorwitz) * (Python committer) Date: 2003-06-20 17:18
Logged In: YES 
user_id=33168

unicode, list, and dict don't crash, only string.
msg44087 - (view) Author: Neal Norwitz (nnorwitz) * (Python committer) Date: 2003-06-20 21:23
Logged In: YES 
user_id=33168

I think the problem is that strings are variable length. 
clear_slots() doesn't handle this condtion.  The attached
patch fixes the crash and works fine under valgrind, but I'm
not entirely sure it's correct.  Hopefully someone more
familiar with the TypeObject code can review this.

I'll add a test later.
msg44088 - (view) Author: Neal Norwitz (nnorwitz) * (Python committer) Date: 2003-06-20 22:05
Logged In: YES 
user_id=33168

Ain't testing grand.  Found a couple more problems.  Patch 2
is better, but I'm still not sure it's complete or correct.

I'm thinking the slot offset should be fixed when it's set,
rather than at each usage of the slot offset.
msg44089 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2003-07-09 16:13
Logged In: YES 
user_id=6380

Neal, I don't think your patch can work, unless you
change the *descriptors* used for slots also:
the slot offsets used by the slot descriptors
will still be constant and hence overwrite the
characters of the string (see structmember.c).
Example (with your patch):

>>> class C(str): __slots__ = ['a'] 
...      
>>> s = C("hello world") 
>>> s.a 
'\x00\x00\x00\x00\x90\x86\x12\x08\x00\x00' 
>>>  

I'd rather see the 2.2 situation back, where
it's simply illegal to use __slots__ in a str subclass
(or any subclass of a class whose tp_itemsize
!= 0).
msg44090 - (view) Author: Neal Norwitz (nnorwitz) * (Python committer) Date: 2003-07-15 21:06
Logged In: YES 
user_id=33168

Jeremy, I agree with Guido, I think nonempty slots shouldn't
be allowed for strings.  I think this is just a matter of
removing && !PyType_Check(base) in Objects/typeobject.c
around line 1656.  I don't have time to test it and I'm not
sure what the ramifications of this change are.  Can you get
this into 2.3?
msg44091 - (view) Author: Jeremy Hylton (jhylton) (Python triager) Date: 2003-07-16 17:09
Logged In: YES 
user_id=31392

My checkin 2.240 had the side-effect of restoring the
behavior we got in 2.2.x.  My checkin comment wasn't very
clear about this (nor was the 2.215 comment that added this
feature).

Somehow the check for whether slots were allowed added a
condition that could never be true.  So there was no way to
get the TypeError "nonempty __slots__ not supported for
subtype of '%s'".

I don't think the change was intentional.  In addition to
the string crash, it created the possiblity for mutable tuples:
>>> class T(tuple):
...     __slots__ = "x", "y"
... 
>>> t = T((1, 2))
>>> t
(1, 2)
>>> t.x
1
>>> t.y
2
>>> t.y = -1
>>> t
(1, -1)

History
Date User Action Args
2022-04-10 16:09:22adminsetgithub: 38693
2003-06-20 16:28:30jpecreate