# Read\_multipart bug (in Ruby's CGI and Rail's AbstractRequest

**URL:** <https://www.ruby-forum.com/t/read-multipart-bug-in-rubys-cgi-and-rails-abstractrequest/105719>\
**Category:** Ruby\
**Created:** [June 1, 2007, 6:45pm UTC](https://www.ruby-forum.com/t/read-multipart-bug-in-rubys-cgi-and-rails-abstractrequest/105719 "2007-06-01T18:45:38Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [June 1, 2007, 6:45pm UTC](https://www.ruby-forum.com/t/read-multipart-bug-in-rubys-cgi-and-rails-abstractrequest/105719/1 "2007-06-01T18:45:38Z")

</div>

I have written code that receives multipart forms. I’m currently  
testing the software by using curl to push files through a Rails  
controller. One of my test cases fails, and it strongly looks like  
the failure is due to incorrect code in read\_multipart:

…  
bufsize = 10 \* 1024  
…  
buf =  
buf.sub(/\A((?:.|\n)\*?)(?:[\r\n]{1,2})?#{quoted\_boundary}([\r\n]{1,2}|–)/n)  
do  
body.print $1  
if “–” == $2  
content\_length = -1  
end  
boundary\_end = $2.dup  
“”  
end  
…  
break if buf.size == 0  
break if content\_length === -1  
end  
raise EOFError, “bad boundary end of body part” unless  
boundary\_end=~/–/

The problem appears to be that if a bufsize chunk is read that matches  
the “buf.sub” regular expression above exactly, buf can become “”,  
which has size 0, even though there is more information to be read.  
This can leave $2 set to “\r\n” with buf.size 0, so the EOFError  
exception is raised, even though there are more bytes to read  
(including the “–”).

If I change bufsize to be 100 \* 1024, or 4 \* 1024, the problem goes  
away, because I no longer get a buffer that matches the pattern  
exactly. If I comment out the “break if buf.size == 0” the problem  
goes away, because the code can keep going and find the “–” it’s  
looking for. Specifically, if I comment out the “break if buf.size ==  
0”  
and instrument the code to show me some of the values of the variables  
I get the output that I have after my signature. In it, it’s clear  
that $2 is “\r\n” and buf.size is 0, even though there’s more to come,  
but since we don’t break on buf.size 0, the loop goes around once more  
and picks up the extra bytes.

Looking at all of read\_multipart, I’m not sure why the  
“break if buf.size == 0” code is there, since earlier in the code an  
EOFError will be raised if body.read returns nil or “”. On the other  
hand, the exit may be necessary to prevent some sort of infinite loop  
not associated body.read running into the end of the file.

I’m posting this to the Ruby mailing list, because I suspect there are  
people on the list intimately familiar with read\_multipart who might  
be able to see the problem and fix it properly. The particular data  
set that I’m exercising contains sensitive data, but if there’s  
interest, I can capture curl’s output and scrub the data, although I’m  
not sufficiently familiar with CGI to know how to write a unit test  
for read\_multipart.

I’m cc’ing this to the Rails mailing list because Rails has its own  
copy of the CGI code in actionpack/lib/action\_controller/request.rb,  
and that’s where I first encountered the bug.

–  
Cliff M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
Stolen Bases  
aka removed\_email\_address@domain.invalid

Instrumented code:

…  
File.new(‘/tmp/request.log’,‘a’).syswrite(“quoted\_boundary =  
"#{quoted\_boundary}", buf = #{buf}\n”)  
buf =  
buf.sub(/\A((?:.|\n)\*?)(?:[\r\n]{1,2})?#{quoted\_boundary}([\r\n]{1,2}|–)/n)  
do  
…  
boundary\_end = $2.dup  
File.new(‘/tmp/request.log’,‘a’).syswrite(“boundary\_end =  
#{boundary\_end}, $2 = #{$2.inspect}\n”)  
“”  
…  
File.new(‘/tmp/request.log’,‘a’).syswrite(“buf.size =  
#{buf.size}, content\_length = #{content\_length}\n”)

# break if buf.size == 0

```
        break if content_length == -1
      end
      File.new('/tmp/request.log','a').syswrite("raise? boundary_end 

```

= #{boundary\_end}\n")

```
      raise EOFError, "bad boundary end of body part" unless 

```

boundary\_end=~/–/

Output from instrumented code:

quoted\_boundary =  
“------------------------------d138ece8d971”,  
buf = \<\>

------------------------------d138ece8d971

$2 = “\r\n”  
buf.size = 0, content\_length = 124  
quoted\_boundary =  
“------------------------------d138ece8d971”,  
buf = application/pdf

------------------------------d138ece8d971–

$2 = “–”  
buf.size = 2, content\_length = -1
