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

cmd/vet: flag using %s:%d to construct network addresses #28308

Open
stapelberg opened this issue Oct 22, 2018 · 1 comment
Open

cmd/vet: flag using %s:%d to construct network addresses #28308

stapelberg opened this issue Oct 22, 2018 · 1 comment

Comments

@stapelberg
Copy link
Contributor

@stapelberg stapelberg commented Oct 22, 2018

I recently diagnosed a bug in someone’s Go program where a user reported that they couldn’t get the program to connect, and it turned out the issue was that the programmer used fmt.Sprintf("%s:%d", host, port) instead of net.JoinHostPort to construct a network address to pass to net.Dial: https://twitter.com/zekjur/status/1049642773278650368. The issue is subtle: net.JoinHostPort correctly handles IPv6 address literals (e.g. 2001:4860:4860::8888), whereas fmt.Sprintf doesn’t.

A very similar issue is to use strings.Split instead of net.SplitHostPort.

I noticed that both of these issues are fairly prevalent, likely because programmers aren’t that accustomed to using IPv6 literals yet, but I expect them to become more common as IPv6 adoption continues to grow.

I propose adding a vet check to flag using %s:%d format strings with arguments whose names contain port, addr, host, listen or bind. This heuristic will flag 12356 occurrences¹ I found on GitHub using BigQuery, and hopefully make programmers aware not only of net.JoinHostPort but also net.SplitHostPort, for which writing a check is significantly harder.

I can send a CL to implement this. Let me know what you think.

① The BigQuery query I used was:

WITH lines AS
  (SELECT
    id,
    SPLIT(content, "\n") AS line
  FROM `gocontents.gocontents`)
SELECT path, flattened_line
FROM lines
CROSS JOIN UNNEST(lines.line) AS flattened_line
JOIN `gocontents.gofiles` AS files ON files.id = lines.id
WHERE
  REGEXP_CONTAINS(flattened_line, r"fmt.Sprintf\(\"%s:%d\", (port|addr|host|listen|bind)")
@mvdan
Copy link
Member

@mvdan mvdan commented Oct 23, 2018

This definitely checks the correctness and frequency requirements for a vet check - the issue leads to bugs, and it happens often.

However, I'm not sure if it ticks off the precision requirement. If the check simply inspects the syntax tree, it could be prone to false positives or negatives. Dominik proposes a slightly different approach for his tooling in dominikh/go-tools#358, for example.

@bcmills bcmills added this to the Unplanned milestone Oct 23, 2018
@bcmills bcmills added the Proposal label Oct 23, 2018
lisa added a commit to openshift/managed-cluster-validating-webhooks that referenced this issue Aug 21, 2020
tklauser added a commit to cilium/cilium that referenced this issue Aug 26, 2020
Using fmt.Sprintf("%s:%d", host, port) or similar to construct network
address strings can to problems when dealing with IPv6 addresses.
However, net.JoinHostPort formats IPv6 in the correct [host]:port
notation, for example [fe80::a46c:bcff:feed:a481]:8080

For consistency, also use the same function in places where it's obvious
we're dealing with IPv4 addresses.

Also see golang/go#28308

Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
tklauser added a commit to cilium/cilium that referenced this issue Aug 26, 2020
Using fmt.Sprintf("%s:%d", host, port) or similar to construct network
address strings can to problems when dealing with IPv6 addresses.
However, net.JoinHostPort formats IPv6 in the correct [host]:port
notation, for example [fe80::a46c:bcff:feed:a481]:8080

For consistency, also use the same function in places where it's obvious
we're dealing with IPv4 addresses.

Also see golang/go#28308

Found using semgrep using the hostport.yml rule from
https://github.com/dgryski/semgrep-go

Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
kkourt added a commit to cilium/cilium that referenced this issue Sep 4, 2020
Using fmt.Sprintf("%s:%d", host, port) or similar to construct network
address strings can to problems when dealing with IPv6 addresses.
However, net.JoinHostPort formats IPv6 in the correct [host]:port
notation, for example [fe80::a46c:bcff:feed:a481]:8080

For consistency, also use the same function in places where it's obvious
we're dealing with IPv4 addresses.

Also see golang/go#28308

Found using semgrep using the hostport.yml rule from
https://github.com/dgryski/semgrep-go

Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
pchaigno added a commit to cilium/cilium that referenced this issue Oct 22, 2020
[ upstream commit 070214f ]

Using fmt.Sprintf("%s:%d", host, port) or similar to construct network
address strings can to problems when dealing with IPv6 addresses.
However, net.JoinHostPort formats IPv6 in the correct [host]:port
notation, for example [fe80::a46c:bcff:feed:a481]:8080

For consistency, also use the same function in places where it's obvious
we're dealing with IPv4 addresses.

Also see golang/go#28308

Found using semgrep using the hostport.yml rule from
https://github.com/dgryski/semgrep-go

Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
Signed-off-by: Paul Chaignon <paul@cilium.io>
pchaigno added a commit to cilium/cilium that referenced this issue Oct 22, 2020
[ upstream commit 070214f ]

Using fmt.Sprintf("%s:%d", host, port) or similar to construct network
address strings can to problems when dealing with IPv6 addresses.
However, net.JoinHostPort formats IPv6 in the correct [host]:port
notation, for example [fe80::a46c:bcff:feed:a481]:8080

For consistency, also use the same function in places where it's obvious
we're dealing with IPv4 addresses.

Also see golang/go#28308

Found using semgrep using the hostport.yml rule from
https://github.com/dgryski/semgrep-go

Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
Signed-off-by: Paul Chaignon <paul@cilium.io>
pchaigno added a commit to cilium/cilium that referenced this issue Oct 27, 2020
[ upstream commit 070214f ]

Using fmt.Sprintf("%s:%d", host, port) or similar to construct network
address strings can to problems when dealing with IPv6 addresses.
However, net.JoinHostPort formats IPv6 in the correct [host]:port
notation, for example [fe80::a46c:bcff:feed:a481]:8080

For consistency, also use the same function in places where it's obvious
we're dealing with IPv4 addresses.

Also see golang/go#28308

Found using semgrep using the hostport.yml rule from
https://github.com/dgryski/semgrep-go

Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
Signed-off-by: Paul Chaignon <paul@cilium.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants