Navigation Menu

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

io: LimitReader increases limit with negative Read count #22214

Closed
superstas opened this issue Oct 11, 2017 · 8 comments
Closed

io: LimitReader increases limit with negative Read count #22214

superstas opened this issue Oct 11, 2017 · 8 comments
Labels
FrozenDueToAge NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.

Comments

@superstas
Copy link
Contributor

What version of Go are you using (go version)?

go version go1.8.3 linux/amd64

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

GOARCH="amd64"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GORACE=""
GOROOT="/usr/lib/go-1.8"
GOTOOLDIR="/usr/lib/go-1.8/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build916457943=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
PKG_CONFIG="pkg-config"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"

What did you do?

There is a potential bug in io.LimitReader in case of bad io.Reader implementation that returns negative read bytes. In that case variable N in io.LimitReader increase ( but it should decrease ). In the end it reads more bytes than has to.

This is not critical bug, but in some cases it might be quite seriously, even despite bad implementation of io.Reader.

Here is a demo of the bug: https://play.golang.org/p/2PYFi7J0CR

I think it would be reasonable to check read bytes before N decreasing, and do return in case of negative reading without decreasing

What did you expect to see?

I want to read 8 bytes

What did you see instead?

I receive 9 bytes.

ping @dsnet

@as
Copy link
Contributor

as commented Oct 11, 2017

If I'm relying on io.LimitedReader to protect a region of memory, then it can be a critical issue when used with io.TeeReader

io.TeeReader(io.LimitReader(n), buf)) // constraint: no access to buf beyond n

@ianlancetaylor ianlancetaylor added the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label Oct 11, 2017
@dsnet
Copy link
Member

dsnet commented Oct 11, 2017

In #22097, I mentioned that this is a slippery slope and I'm hesitant to add further checks unless we consciously make a decision that we want all functions that take in an io.Reader to protect against invalid readers (and writers).

Adding the checks to bufio.Reader and bytes.Buffer.ReadFrom was fine since those are probably the most common vectors through which a bad reader would be detected.

There are several broader question at hand:

  • Should users of io.Reader and io.Writer be responsible for checking non-compliant implementations?
    • If so, what type of non-compliant issues do we check for? Negative counts are hardly the only issue.
      • Do we check that Read returns a count within 0 <= n <= len(b)?
      • Do we check that Write returns a count within 0 <= n <= len(b)?
      • Do we check that Write returns n == len(b) if err == nil?
    • What do you do when you discover an error? Do we panic (that's what bufio.Reader does). Do we convert it into an error? Do you treat a negative count as equivalent to zero?
  • Where should we perform these checks?
    • I can see at least the following needing to be addressed: io.Copy, io.CopyBuffer, io.CopyN, io.ReadAtLeast, io.ReadFull, io.LimitedReader, io.MultiReader, io.TeeReader, io.SectionReader, ioutil.ReadAll, etc...

Given the large scope creep that adding these checks will cause, I'm hesitant to penalize all users of io.Reader to handle invalid implementations.

@ianlancetaylor
Copy link
Contributor

I don't even know what LimitReader means for a broken Reader. I don't see why any particular behavior is correct.

@superstas superstas changed the title io: LimitReader decreases limit with negative Read count io: LimitReader increases limit with negative Read count Oct 11, 2017
@dsnet
Copy link
Member

dsnet commented Oct 11, 2017

If your code base is encountering a number of invalid implementations of io.Reader, I would suggest wrapping your readers in CheckReader and see what types of things fail.

Even better, for all the implementations of io.Reader, do something similar in your unit tests.

@ianlancetaylor
Copy link
Contributor

I'm going to decline this issue. We are not going to add checks in the standard library for all possible erroneous behavior by io.Reader values.

@superstas
Copy link
Contributor Author

@dsnet

  1. Your concerns are valid. On the one hand I agree that is a slippery slope, but I don't like that clients should totally trust the io.Reader contract only. On the other hand that is a protocol that have been agreed by both sides for communication.

  2. I like the idea with CheckReader/CheckWriter, and I would add a check to consecutive empty reads with 0, nil ( e.g. 100 consecutive empty reads, as bufio.maxConsecutiveEmptyReads )

What do you think, should we add those checkers to io package?
I believe that it might be a good way of checking an io.Reader implementation ( the only source of the truth; automatic self-test in units ioutil.ReadAll(io.CheckReader(CustomReader))) )

@dsnet
Copy link
Member

dsnet commented Oct 11, 2017

Those types should not be added to io since they are primarily for testing and their utility at large is pretty low. It could possibly be added to the testing/iotest package, but I personally find that package awkward and is often more problematic than helpful.

Given the simplicity of the code, it's not bad maintaining it yourself. I don't see a strong need for it to be in the standard library.

@superstas
Copy link
Contributor Author

@dsnet thank you for explanation

@golang golang locked and limited conversation to collaborators Oct 11, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Projects
None yet
Development

No branches or pull requests

5 participants