'NoneType' object has no attribute 'encode' in requestReceived() when multipart body doesn't include content-disposition

Bug #1915819 reported by Victor Tapia
28
This bug affects 3 people
Affects Status Importance Assigned to Milestone
twisted (Debian)
Fix Released
Unknown
twisted (Ubuntu)
Fix Released
Medium
Victor Tapia
Focal
Fix Released
Medium
Victor Tapia
Groovy
Fix Released
Medium
Victor Tapia
Hirsute
Fix Released
Medium
Victor Tapia

Bug Description

[impact]

python-twisted errors out with "'NoneType' object has no attribute 'encode' in requestReceived()" when it tries to parse a multipart mime message and python3.7+ is used. This happens because before commit cc3fa20 in cpython, cgi.parse_multipart ignored parts without a name defined in "content-disposition" (or parts without headers for that matter) but after 3.7+ the return of the function can now contain NoneType keys, which fail to encode.

[scope]

Even though this bug affects all python3-twisted releases, I'll backport the fix just to Focal, Groovy and Hirsute. Bionic and Xenial do not have Python 3.7 as the default interpreter (required to trigger the issue), and the delta in python3-twisted might be to big to backport as the current packages do not contemplate _PY37PLUS at all.

Fixed upstream with commit 310496249, available since 21.2.0rc1

[test case]

1. Save the following code as webserver.py

from twisted.application.internet import TCPServer
from twisted.application.service import Application
from twisted.web.resource import Resource
from twisted.web.server import Site

class Foo(Resource):
    def render_POST(self, request):
        newdata = request.content.getvalue()
        print(newdata)
        return ''

root = Resource()
root.putChild("foo", Foo())
application = Application("cgi.parse_multipart test")
TCPServer(8080, Site(root)).setServiceParent(application)

2. Save the following code as client.py (python3-httplib2 is required)

#!/usr/bin/env python
import httplib2

def http_request(url, method, body=None, headers=None, insecure=False):
    """Issue an http request."""
    http = httplib2.Http(disable_ssl_certificate_validation=insecure)
    if isinstance(url, bytes):
        url = url.decode("ascii")
    return http.request(url, method, body=body, headers=headers)

url = "http://localhost:8080"
method = "POST"
headers = {'Content-Type': 'multipart/form-data; boundary="8825899812428059282"'}
emptyh = '--8825899812428059282\n\n--8825899812428059282--'

print("== BODY: " + emptyh + "\n")
response, content = http_request(url, method, emptyh, headers)

3. Run the server with "twistd3 -y webserver.py"
4. Run the client
5. twistd will fail to encode the key and will drop this traceback in the log file (twistd.log)

2021-02-16T13:41:39+0100 [_GenericHTTPChannelProtocol,7,127.0.0.1] Unhandled Error
        Traceback (most recent call last):
          File "/usr/lib/python3/dist-packages/twisted/python/log.py", line 103, in callWithLogger
            return callWithContext({"system": lp}, func, *args, **kw)
          File "/usr/lib/python3/dist-packages/twisted/python/log.py", line 86, in callWithContext
            return context.call({ILogContext: newCtx}, func, *args, **kw)
          File "/usr/lib/python3/dist-packages/twisted/python/context.py", line 122, in callWithContext
            return self.currentContext().callWithContext(ctx, func, *args, **kw)
          File "/usr/lib/python3/dist-packages/twisted/python/context.py", line 85, in callWithContext
            return func(*args,**kw)
        --- <exception caught here> ---
          File "/usr/lib/python3/dist-packages/twisted/internet/posixbase.py", line 614, in _doReadOrWrite
            why = selectable.doRead()
          File "/usr/lib/python3/dist-packages/twisted/internet/tcp.py", line 243, in doRead
            return self._dataReceived(data)
          File "/usr/lib/python3/dist-packages/twisted/internet/tcp.py", line 249, in _dataReceived
            rval = self.protocol.dataReceived(data)
          File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2952, in dataReceived
            return self._channel.dataReceived(data)
          File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2245, in dataReceived
            return basic.LineReceiver.dataReceived(self, data)
          File "/usr/lib/python3/dist-packages/twisted/protocols/basic.py", line 579, in dataReceived
            why = self.rawDataReceived(data)
          File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2252, in rawDataReceived
            self._transferDecoder.dataReceived(data)
          File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 1699, in dataReceived
            finishCallback(data[contentLength:])
          File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2115, in _finishRequestBody
            self.allContentReceived()
          File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 2224, in allContentReceived
            req.requestReceived(command, path, version)
          File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 898, in requestReceived
            self.args.update({
          File "/usr/lib/python3/dist-packages/twisted/web/http.py", line 899, in <dictcomp>
            x.encode('iso-8859-1'): \
        builtins.AttributeError: 'NoneType' object has no attribute 'encode'

[regression potential]

This affects the returned dictionaries with non-str keys, which were discarded in python3.6 or earlier before they reached twisted, so patching this will make its behavior consistent.

Victor Tapia (vtapia)
description: updated
Revision history for this message
Victor Tapia (vtapia) wrote :
Revision history for this message
Victor Tapia (vtapia) wrote :
Revision history for this message
Victor Tapia (vtapia) wrote :
Revision history for this message
Ubuntu Foundations Team Bug Bot (crichton) wrote :

The attachment "hirsute.debdiff" seems to be a debdiff. The ubuntu-sponsors team has been subscribed to the bug report so that they can review and hopefully sponsor the debdiff. If the attachment isn't a patch, please remove the "patch" flag from the attachment, remove the "patch" tag, and if you are member of the ~ubuntu-sponsors, unsubscribe the team.

[This is an automated message performed by a Launchpad user owned by ~brian-murray, for any issue please contact him.]

tags: added: patch
Dan Streetman (ddstreet)
tags: added: sts-sponsor-ddstreet
Changed in twisted (Ubuntu Hirsute):
assignee: nobody → Victor Tapia (vtapia)
Changed in twisted (Ubuntu Groovy):
assignee: nobody → Victor Tapia (vtapia)
Changed in twisted (Ubuntu Focal):
assignee: nobody → Victor Tapia (vtapia)
Changed in twisted (Ubuntu Hirsute):
importance: Undecided → Medium
Changed in twisted (Ubuntu Groovy):
importance: Undecided → Medium
Changed in twisted (Ubuntu Focal):
importance: Undecided → Medium
Changed in twisted (Ubuntu Hirsute):
status: New → In Progress
Changed in twisted (Ubuntu Groovy):
status: New → In Progress
Changed in twisted (Ubuntu Focal):
status: New → In Progress
Revision history for this message
Dan Streetman (ddstreet) wrote :

uploaded to f/g/h thanks!

Revision history for this message
Łukasz Zemczak (sil2100) wrote : Please test proposed package

Hello Victor, or anyone else affected,

Accepted twisted into groovy-proposed. The package will build now and be available at https://launchpad.net/ubuntu/+source/twisted/18.9.0-11ubuntu0.20.10.1 in a few hours, and then in the -proposed repository.

Please help us by testing this new package. See https://wiki.ubuntu.com/Testing/EnableProposed for documentation on how to enable and use -proposed. Your feedback will aid us getting this update out to other Ubuntu users.

If this package fixes the bug for you, please add a comment to this bug, mentioning the version of the package you tested, what testing has been performed on the package and change the tag from verification-needed-groovy to verification-done-groovy. If it does not fix the bug for you, please add a comment stating that, and change the tag to verification-failed-groovy. In either case, without details of your testing we will not be able to proceed.

Further information regarding the verification process can be found at https://wiki.ubuntu.com/QATeam/PerformingSRUVerification . Thank you in advance for helping!

N.B. The updated package will be released to -updates after the bug(s) fixed by this package have been verified and the package has been in -proposed for a minimum of 7 days.

Changed in twisted (Ubuntu Groovy):
status: In Progress → Fix Committed
tags: added: verification-needed verification-needed-groovy
Revision history for this message
Łukasz Zemczak (sil2100) wrote :

Hello Victor, or anyone else affected,

Accepted twisted into focal-proposed. The package will build now and be available at https://launchpad.net/ubuntu/+source/twisted/18.9.0-11ubuntu0.20.04.1 in a few hours, and then in the -proposed repository.

Please help us by testing this new package. See https://wiki.ubuntu.com/Testing/EnableProposed for documentation on how to enable and use -proposed. Your feedback will aid us getting this update out to other Ubuntu users.

If this package fixes the bug for you, please add a comment to this bug, mentioning the version of the package you tested, what testing has been performed on the package and change the tag from verification-needed-focal to verification-done-focal. If it does not fix the bug for you, please add a comment stating that, and change the tag to verification-failed-focal. In either case, without details of your testing we will not be able to proceed.

Further information regarding the verification process can be found at https://wiki.ubuntu.com/QATeam/PerformingSRUVerification . Thank you in advance for helping!

N.B. The updated package will be released to -updates after the bug(s) fixed by this package have been verified and the package has been in -proposed for a minimum of 7 days.

Changed in twisted (Ubuntu Focal):
status: In Progress → Fix Committed
tags: added: verification-needed-focal
Revision history for this message
Launchpad Janitor (janitor) wrote :

This bug was fixed in the package twisted - 20.3.0-4ubuntu1

---------------
twisted (20.3.0-4ubuntu1) hirsute; urgency=medium

  * Fix NoneType encode error when multipart body does not include
    content-disposition headers (LP: #1915819)
    - d/p/lp1915819-Fix-nonetype-encode-error.patch

 -- Victor Manuel Tapia King <email address hidden> Wed, 17 Feb 2021 12:48:20 +0100

Changed in twisted (Ubuntu Hirsute):
status: In Progress → Fix Released
Revision history for this message
Ubuntu SRU Bot (ubuntu-sru-bot) wrote : Autopkgtest regression report (twisted/18.9.0-11ubuntu0.20.04.1)

All autopkgtests for the newly accepted twisted (18.9.0-11ubuntu0.20.04.1) for focal have finished running.
The following regressions have been reported in tests triggered by the package:

apport/2.20.11-0ubuntu27.16 (amd64)
automat/0.8.0-1ubuntu1 (arm64, amd64, ppc64el, armhf, s390x)

Please visit the excuses page listed below and investigate the failures, proceeding afterwards as per the StableReleaseUpdates policy regarding autopkgtest regressions [1].

https://people.canonical.com/~ubuntu-archive/proposed-migration/focal/update_excuses.html#twisted

[1] https://wiki.ubuntu.com/StableReleaseUpdates#Autopkgtest_Regressions

Thank you!

Revision history for this message
Victor Tapia (vtapia) wrote :

#VERIFICATION-DONE-GROOVY

Using the webserver.py/client.py scripts defined in the test case section in the description:

$ dpkg -l | grep twisted
ii python3-twisted 18.9.0-11ubuntu0.20.10.1 all Event-based framework for internet applications
ii python3-twisted-bin:amd64 18.9.0-11ubuntu0.20.10.1 amd64 Event-based framework for internet applications

$ twistd3 -y webserver.py

$ ./client.py
== BODY: --8825899812428059282

--8825899812428059282--

The multipart request works fine (it's logged at the end) and there's no exception thrown to the logs:

$ tail -n10 twistd.log
2021-02-26T12:22:39+0100 [-] Loading webserver.py...
2021-02-26T12:22:39+0100 [-] Loaded.
2021-02-26T12:22:39+0100 [twisted.scripts._twistd_unix.UnixAppLogger#info] twistd 18.9.0 (/usr/bin/python3 3.8.6) starting up.
2021-02-26T12:22:39+0100 [twisted.scripts._twistd_unix.UnixAppLogger#info] reactor class: twisted.internet.epollreactor.EPollReactor.
2021-02-26T12:22:39+0100 [-] Site starting on 8080
2021-02-26T12:22:39+0100 [twisted.web.server.Site#info] Starting factory <twisted.web.server.Site object at 0x7f1a8a192df0>
2021-02-26T12:22:45+0100 [twisted.python.log#info] 127.0.0.1 - - [26/Feb/2021:11:22:44 +0000] "POST / HTTP/1.1" 404 153 "-" "Python-httplib2/0.18.1 (gzip)"

Revision history for this message
Victor Tapia (vtapia) wrote :

#VERIFICATION-DONE-FOCAL

Using the webserver.py/client.py scripts defined in the test case section in the description:

$ dpkg -l | grep twisted
ii python3-twisted 18.9.0-11ubuntu0.20.04.1 all Event-based framework for internet applications
ii python3-twisted-bin:amd64 18.9.0-11ubuntu0.20.04.1 amd64 Event-based framework for internet applications

$ twistd3 -y ./webserver.py

$ ./client.py
== BODY: --8825899812428059282

--8825899812428059282--

The multipart request works fine (it's logged at the end) and there's no exception thrown to the logs:

$ tail ./twistd.log
2021-02-26T13:56:41+0100 [-] Loading ./webserver.py...
2021-02-26T13:56:42+0100 [-] Loaded.
2021-02-26T13:56:42+0100 [twisted.scripts._twistd_unix.UnixAppLogger#info] twistd 18.9.0 (/usr/bin/python3 3.8.5) starting up.
2021-02-26T13:56:42+0100 [twisted.scripts._twistd_unix.UnixAppLogger#info] reactor class: twisted.internet.epollreactor.EPollReactor.
2021-02-26T13:56:42+0100 [-] Site starting on 8080
2021-02-26T13:56:42+0100 [twisted.web.server.Site#info] Starting factory <twisted.web.server.Site object at 0x7fc2ccad4f10>
2021-02-26T13:56:46+0100 [twisted.python.log#info] 127.0.0.1 - - [26/Feb/2021:12:56:46 +0000] "POST / HTTP/1.1" 404 153 "-" "Python-httplib2/0.14.0 (gzip)"

# AUTOPKGTEST NOTES
Autopkgtest failures in automat are due to a missing package in Ubuntu: python-twisted-core is missing in Focal (as the rest of python2 packages) but is required by automat tests. This has been reported in the following bug:

https://bugs.launchpad.net/ubuntu/+source/automat/+bug/1917041

tags: added: verification-done verification-done-focal verification-done-groovy
removed: verification-needed verification-needed-focal verification-needed-groovy
Revision history for this message
Launchpad Janitor (janitor) wrote :

This bug was fixed in the package twisted - 18.9.0-11ubuntu0.20.10.1

---------------
twisted (18.9.0-11ubuntu0.20.10.1) groovy; urgency=medium

  * Fix NoneType encode error when multipart body does not include
    content-disposition headers (LP: #1915819)
    - d/p/lp1915819-Fix-nonetype-encode-error.patch

 -- Victor Manuel Tapia King <email address hidden> Wed, 17 Feb 2021 18:00:36 +0100

Changed in twisted (Ubuntu Groovy):
status: Fix Committed → Fix Released
Revision history for this message
Brian Murray (brian-murray) wrote : Update Released

The verification of the Stable Release Update for twisted has completed successfully and the package is now being released to -updates. Subsequently, the Ubuntu Stable Release Updates Team is being unsubscribed and will not receive messages about this bug report. In the event that you encounter a regression using the package from -updates please report a new bug using ubuntu-bug and tag the bug report regression-update so we can easily find any regressions.

Revision history for this message
Launchpad Janitor (janitor) wrote :

This bug was fixed in the package twisted - 18.9.0-11ubuntu0.20.04.1

---------------
twisted (18.9.0-11ubuntu0.20.04.1) focal; urgency=medium

  * Fix NoneType encode error when multipart body does not include
    content-disposition headers (LP: #1915819)
    - d/p/lp1915819-Fix-nonetype-encode-error.patch

 -- Victor Manuel Tapia King <email address hidden> Wed, 17 Feb 2021 14:46:53 +0100

Changed in twisted (Ubuntu Focal):
status: Fix Committed → Fix Released
Revision history for this message
David Andruczyk (dandruczyk) wrote :

This breaks the maas snap in a bad way for API heavy users which appears to have the broken version of python-twisted embedded within it:
maas 2.9.2-9164-g.ac176b5c4 11851 2.9/stable canonical* -

See https://bugs.launchpad.net/maas/+bug/1918206

Revision history for this message
Dan Streetman (ddstreet) wrote :

> This breaks the maas snap

I believe the maas snap should pick this fix up in its next build

Changed in twisted (Debian):
status: Unknown → New
Dan Streetman (ddstreet)
tags: removed: sts-sponsor-ddstreet
Changed in twisted (Debian):
status: New → Fix Released
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Duplicates of this bug

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.