Skip to content

Commit c6366e5

Browse files
Emyrkmafredri
andauthored
chore: prevent nil derefs in non-critical paths (#11411)
* chore: prevent nil derefs in non-critical paths --------- Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
1 parent 85ff030 commit c6366e5

File tree

1 file changed

+24
-5
lines changed

1 file changed

+24
-5
lines changed

cli/cliui/agent.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,12 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
200200

201201
switch agent.LifecycleState {
202202
case codersdk.WorkspaceAgentLifecycleReady:
203-
sw.Complete(stage, agent.ReadyAt.Sub(*agent.StartedAt))
203+
sw.Complete(stage, safeDuration(sw, agent.ReadyAt, agent.StartedAt))
204204
case codersdk.WorkspaceAgentLifecycleStartTimeout:
205205
sw.Fail(stage, 0)
206206
sw.Log(time.Time{}, codersdk.LogLevelWarn, "Warning: A startup script timed out and your workspace may be incomplete.")
207207
case codersdk.WorkspaceAgentLifecycleStartError:
208-
sw.Fail(stage, agent.ReadyAt.Sub(*agent.StartedAt))
208+
sw.Fail(stage, safeDuration(sw, agent.ReadyAt, agent.StartedAt))
209209
// Use zero time (omitted) to separate these from the startup logs.
210210
sw.Log(time.Time{}, codersdk.LogLevelWarn, "Warning: A startup script exited with an error and your workspace may be incomplete.")
211211
sw.Log(time.Time{}, codersdk.LogLevelWarn, troubleshootingMessage(agent, "https://coder.com/docs/v2/latest/templates#startup-script-exited-with-an-error"))
@@ -221,7 +221,7 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
221221
case agent.LifecycleState.ShuttingDown():
222222
// We no longer know if the startup script failed or not,
223223
// but we need to tell the user something.
224-
sw.Complete(stage, agent.ReadyAt.Sub(*agent.StartedAt))
224+
sw.Complete(stage, safeDuration(sw, agent.ReadyAt, agent.StartedAt))
225225
return errAgentShuttingDown
226226
}
227227
}
@@ -238,13 +238,13 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO
238238
sw.Log(time.Now(), codersdk.LogLevelWarn, "Wait for it to reconnect or restart your workspace.")
239239
sw.Log(time.Now(), codersdk.LogLevelWarn, troubleshootingMessage(agent, "https://coder.com/docs/v2/latest/templates#agent-connection-issues"))
240240

241-
disconnectedAt := *agent.DisconnectedAt
241+
disconnectedAt := agent.DisconnectedAt
242242
for agent.Status == codersdk.WorkspaceAgentDisconnected {
243243
if agent, err = fetch(); err != nil {
244244
return xerrors.Errorf("fetch: %w", err)
245245
}
246246
}
247-
sw.Complete(stage, agent.LastConnectedAt.Sub(disconnectedAt))
247+
sw.Complete(stage, safeDuration(sw, agent.LastConnectedAt, disconnectedAt))
248248
}
249249
}
250250
}
@@ -257,6 +257,25 @@ func troubleshootingMessage(agent codersdk.WorkspaceAgent, url string) string {
257257
return m
258258
}
259259

260+
// safeDuration returns a-b. If a or b is nil, it returns 0.
261+
// This is because we often dereference a time pointer, which can
262+
// cause a panic. These dereferences are used to calculate durations,
263+
// which are not critical, and therefor should not break things
264+
// when it fails.
265+
// A panic has been observed in a test.
266+
func safeDuration(sw *stageWriter, a, b *time.Time) time.Duration {
267+
if a == nil || b == nil {
268+
if sw != nil {
269+
// Ideally the message includes which fields are <nil>, but you can
270+
// use the surrounding log lines to figure that out. And passing more
271+
// params makes this unwieldy.
272+
sw.Log(time.Now(), codersdk.LogLevelWarn, "Warning: Failed to calculate duration from a time being <nil>.")
273+
}
274+
return 0
275+
}
276+
return a.Sub(*b)
277+
}
278+
260279
type closeFunc func() error
261280

262281
func (c closeFunc) Close() error {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy