Bug Summary

File:.build-ci/../nvme-rpmb.c
Warning:line 988, column 41
Potential leak of memory pointed to by 'cfg'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name nvme-rpmb.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/lib/llvm-19/lib/clang/19 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I nvme.p -I . -I .. -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -std=gnu99 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-06-24-175442-590-1 -x c ../nvme-rpmb.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2020 Micron Technology Inc. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 * nvme-rpmb.c - Implementation of NVMe RPMB support commands in Nvme
17 */
18#include <errno(*__errno_location ()).h>
19#include <fcntl.h>
20#include <limits.h>
21#include <stddef.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26
27#include <linux1/if_alg.h>
28#include <linux1/socket.h>
29
30#include <sys/socket.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33
34#include <libnvme.h>
35
36#include "common.h"
37#include "nvme-cmds.h"
38#include "nvme-print.h"
39#include "nvme.h"
40
41#define CREATE_CMD
42
43
44#ifndef AF_ALG38
45#define AF_ALG38 38
46#endif
47#ifndef SOL_ALG279
48#define SOL_ALG279 279
49#endif
50
51#define HMAC_SHA256_ALGO_NAME"hmac(sha256)" "hmac(sha256)"
52#define MD5_HASH_ALGO_NAME"md5" "md5"
53#define HMAC_SHA256_HASH_SIZE32 32
54#define MD5_HASH_HASH_SIZE16 16
55
56/*
57 * Utility function to create hash value of given data (with given key) using
58 * given hash algorithm; this function uses kernel crypto services
59 */
60unsigned char *create_hash(const char *algo,
61 int hash_size,
62 unsigned char *data,
63 int datalen,
64 unsigned char *key,
65 int keylen)
66{
67 int error, infd, outfd = -1;
68 unsigned char *hash = NULL((void*)0);
69 struct sockaddr_alg provider_sa = {
70 .salg_family = AF_ALG38,
71 .salg_type = "hash",
72 .salg_name = { 0 }
73 };
74
75 /* copy algorithm name */
76 if (strlen(algo) > sizeof(provider_sa.salg_name)) {
77 fprintf(stderrstderr, "%s: algorithm name overflow", __func__);
78 return hash;
79 }
80 memcpy(provider_sa.salg_name, algo, strlen(algo));
81
82 /* open netlink socket connection to algorigm provider and bind */
83 infd = socket(AF_ALG38, SOCK_SEQPACKETSOCK_SEQPACKET, 0);
84 if (infd < 0) {
85 perror("socket");
86 return hash;
87 }
88 error = bind(infd, (struct sockaddr *)&provider_sa, sizeof(provider_sa));
89 if (error < 0) {
90 perror("bind");
91 goto out_close_infd;
92 }
93
94 /* if algorithm requires key, set it first - empty keys not accepted !*/
95 if (key != NULL((void*)0) && keylen > 0) {
96 error = setsockopt(infd, SOL_ALG279, ALG_SET_KEY1, key, keylen);
97 if (error < 0) {
98 perror("setsockopt");
99 goto out_close_infd;
100 }
101 }
102
103 /* now send data to hash */
104 outfd = accept(infd, NULL((void*)0), 0);
105 if (outfd < 0) {
106 perror("accept");
107 goto out_close_infd;
108 }
109 error = send(outfd, data, datalen, 0);
110 if (error < 0) {
111 perror("send");
112 goto out_close_outfd;
113 }
114
115 /* read computed hash */
116 hash = (unsigned char *)calloc(hash_size, 1);
117 if (hash == NULL((void*)0)) {
118 perror("calloc");
119 goto out_close_outfd;
120 }
121
122 error = read(outfd, hash, hash_size);
123 if (error != hash_size) {
124 perror("read");
125 free(hash);
126 hash = NULL((void*)0);
127 }
128out_close_outfd:
129 close(outfd);
130out_close_infd:
131 close(infd);
132
133 return hash;
134}
135
136/* Function that computes hmac-sha256 hash of given data and key pair. Returns
137 * byte stream (non-null terminated) upon success, NULL otherwise.
138 */
139unsigned char *hmac_sha256(unsigned char *data, int datalen, unsigned char *key,
140 int keylen)
141{
142 return create_hash(HMAC_SHA256_ALGO_NAME"hmac(sha256)",
143 HMAC_SHA256_HASH_SIZE32,
144 data,
145 datalen,
146 key,
147 keylen);
148}
149
150/* Function that computes md5 of given buffer - md5 hash is used as nonce
151 * Returns byte stream (non-null terminated) upon success, NULL otherwise.
152 */
153unsigned char *rpmb_md5(unsigned char *data, int datalen)
154{
155 return create_hash(MD5_HASH_ALGO_NAME"md5",
156 MD5_HASH_HASH_SIZE16,
157 data,
158 datalen,
159 NULL((void*)0),
160 0);
161}
162
163/* Read data from given file into buffer and return its length */
164static int read_file(const char *file, unsigned char **data, unsigned int *len)
165{
166 struct stat sb;
167 size_t size;
168 unsigned char *buf = NULL((void*)0);
169 int fd;
170 int err = -EINVAL22;
171
172 if (file == NULL((void*)0)) return err;
173
174 if ((fd = open(file, O_RDONLY00)) < 0) {
175 fprintf(stderrstderr, "Failed to open %s: %s\n", file, libnvme_strerror(errno(*__errno_location ())));
176 return fd;
177 }
178
179 err = fstat(fd, &sb);
180 if (err < 0) {
181 perror("fstat");
182 goto out;
183 }
184
185 size = sb.st_size;
186 buf = libnvme_alloc(size);
187 if (!buf) {
188 fprintf(stderrstderr, "No memory for reading file :%s\n", file);
189 err = -ENOMEM12;
190 goto out;
191 }
192
193 err = read(fd, buf, size);
194 if (err < 0) {
195 err = -errno(*__errno_location ());
196 fprintf(stderrstderr, "Failed to read data from file"
197 " %s with %s\n", file, libnvme_strerror(errno(*__errno_location ())));
198 libnvme_free(buf);
199 goto out;
200 }
201 *data = buf;
202 *len = err;
203 err = 0;
204out:
205 close(fd);
206 return err;
207}
208
209/* Write given buffer data to specified file */
210static void write_file(unsigned char *data, size_t len, const char *dir,
211 const char *file, const char *msg)
212{
213 char temp_folder[PATH_MAX4096] = { 0 };
214 __cleanup_file__attribute__((cleanup(cleanup_file))) FILE *fp = NULL((void*)0);
215
216 if (dir != NULL((void*)0))
217 sprintf(temp_folder, "%s/%s", dir, file);
218 else
219 sprintf(temp_folder, "./%s", file);
220
221 if ((fp = fopen(temp_folder, "ab+")) != NULL((void*)0)) {
222 if (fwrite(data, 1, len, fp) != len) {
223 fprintf(stderrstderr, "Failed to write %s data to %s\n",
224 msg ? msg : "", temp_folder);
225 }
226 } else {
227 fprintf(stderrstderr, "Failed to open %s file to write %s\n",
228 temp_folder, msg ? msg : "");
229 }
230}
231
232/* Various definitions used in RPMB related support */
233enum rpmb_request_type {
234 RPMB_REQ_AUTH_KEY_PROGRAM = 0x0001,
235 RPMB_REQ_READ_WRITE_CNTR = 0x0002,
236 RPMB_REQ_AUTH_DATA_WRITE = 0x0003,
237 RPMB_REQ_AUTH_DATA_READ = 0x0004,
238 RPMB_REQ_READ_RESULT = 0x0005,
239 RPMB_REQ_AUTH_DCB_WRITE = 0x0006,
240 RPMB_REQ_AUTH_DCB_READ = 0x0007
241};
242
243enum rpmb_response_type {
244 RPMB_RSP_AUTH_KEY_PROGRAM = (RPMB_REQ_AUTH_KEY_PROGRAM << 8),
245 RPMB_RSP_READ_WRITE_CNTR = (RPMB_REQ_READ_WRITE_CNTR << 8),
246 RPMB_RSP_AUTH_DATA_WRITE = (RPMB_REQ_AUTH_DATA_WRITE << 8),
247 RPMB_RSP_AUTH_DATA_READ = (RPMB_REQ_AUTH_DATA_READ << 8),
248 RPMB_RSP_READ_RESULT = (RPMB_REQ_READ_RESULT << 8),
249 RPMB_RSP_AUTH_DCB_WRITE = (RPMB_REQ_AUTH_DCB_WRITE << 8),
250 RPMB_RSP_AUTH_DCB_READ = (RPMB_REQ_AUTH_DCB_READ << 8)
251};
252
253/* RPMB data frame structure */
254#pragma pack(1)
255struct rpmb_data_frame_t {
256 unsigned char pad[191];
257 unsigned char mac[32];
258 unsigned char target; /* 0-6, should match with NSSF with SS, SR */
259 unsigned char nonce[16];
260 unsigned int write_counter;
261 unsigned int address;
262 unsigned int sectors;
263 unsigned short result;
264 unsigned short type; /* req or response */
265 unsigned char data[0]; /* in sector count times */
266};
267#pragma pack()
268
269struct rpmb_config_block_t {
270 unsigned char bp_enable;
271 unsigned char bp_lock;
272 unsigned char rsvd[510];
273};
274
275#define RPMB_DATA_FRAME_SIZE256 256
276#define RPMB_NVME_SECP0xEA 0xEA
277#define RPMB_NVME_SPSP0x0001 0x0001
278
279static int send_rpmb_req(struct libnvme_transport_handle *hdl, unsigned char tgt,
280 int size, struct rpmb_data_frame_t *req)
281{
282 struct libnvme_passthru_cmd cmd;
283
284 nvme_init_security_send(&cmd, NVME_NSID_NONE, tgt, RPMB_NVME_SPSP0x0001,
285 RPMB_NVME_SECP0xEA, 0, req, size);
286 return libnvme_exec_admin_passthru(hdl, &cmd);
287}
288
289static int recv_rpmb_rsp(struct libnvme_transport_handle *hdl, int tgt, int size,
290 struct rpmb_data_frame_t *rsp)
291{
292 struct libnvme_passthru_cmd cmd;
293
294 nvme_init_security_receive(&cmd, 0, tgt, RPMB_NVME_SPSP0x0001,
295 RPMB_NVME_SECP0xEA, 0, rsp, size);
296 return libnvme_exec_admin_passthru(hdl, &cmd);
297}
298
299/* Initialize nonce value in rpmb request frame */
300static void rpmb_nonce_init(struct rpmb_data_frame_t *req)
301{
302 int num = rand();
303 unsigned char *hash = rpmb_md5((unsigned char *)&num, sizeof(num));
304 if (hash) memcpy(req->nonce, hash, sizeof(req->nonce));
305}
306
307/* Read key from a given key buffer or key file */
308static unsigned char *read_rpmb_key(char *keystr, char *keyfile, unsigned int *keysize)
309{
310 unsigned char *keybuf = NULL((void*)0);
311 int err;
312
313 if (keystr == NULL((void*)0)) {
314 if (keyfile != NULL((void*)0)) {
315 err = read_file(keyfile, &keybuf, keysize);
316 if (err < 0)
317 return NULL((void*)0);
318 }
319 } else if ((keybuf = (unsigned char *)malloc(strlen(keystr))) != NULL((void*)0)) {
320 *keysize = strlen(keystr);
321 memcpy(keybuf, keystr, *keysize);
322 }
323
324 return keybuf;
325}
326
327/* Initialize RPMB request frame with given values */
328static struct rpmb_data_frame_t *
329rpmb_request_init(unsigned int req_size,
330 unsigned short type,
331 unsigned char target,
332 unsigned char nonce,
333 unsigned int addr,
334 unsigned int sectors,
335 unsigned char *data,
336 unsigned short data_offset,
337 unsigned int data_size)
338{
339 struct rpmb_data_frame_t *req = NULL((void*)0);
340
341 if ((req = (struct rpmb_data_frame_t *)calloc(req_size, 1)) == NULL((void*)0)) {
342 fprintf(stderrstderr, "Memory allocation failed for request 0x%04x\n",
343 type);
344 return req;
345 }
346
347 req->type = type;
348 req->target = target;
349 req->address = addr;
350 req->sectors = sectors;
351
352 if (nonce) rpmb_nonce_init(req);
353 if (data) memcpy((unsigned char *)req + data_offset, data, data_size);
354
355 return req;
356}
357
358/* Process rpmb response and print appropriate error message */
359static int check_rpmb_response(struct rpmb_data_frame_t *req,
360 struct rpmb_data_frame_t *rsp, char *msg)
361{
362 const char *rpmb_result_string [] = {
363 "Operation successful",
364 "General failure",
365 "Authentication (MAC) failure",
366 "Counter failure (not matching/incrementing failure)",
367 "Address failure (out of range or wrong alignment)",
368 "Write (data/counter/result) failure",
369 "Read (data/counter/result) failure",
370 "Authentication key not yet programmed",
371 "Invalid device configuration block",
372 "Unknown error"
373 };
374
375 /* check error status before comparing nonce and mac */
376 if (rsp->result != 0) {
377 if (rsp->type != ((req->type << 8) & 0xFF00)) {
378 fprintf(stderrstderr, "%s ! non-matching response 0x%04x for"
379 " 0x%04x\n", msg, rsp->type, req->type);
380 } else if ((rsp->result & 0x80) == 0x80) {
381 fprintf(stderrstderr, "%s ! Expired write-counter !\n", msg);
382 } else if (rsp->result) {
383 fprintf(stderrstderr, "%s ! %s\n", msg,
384 rpmb_result_string[rsp->result & 0x7F]);
385 } else if (memcmp(req->nonce, rsp->nonce, 16)) {
386 fprintf(stderrstderr, "%s ! non-matching nonce\n", msg);
387 } else if (memcmp(req->mac, rsp->mac, 32)) {
388 fprintf(stderrstderr, "%s ! non-matching MAC\n", msg);
389 } else if ((req->write_counter + 1) != rsp->write_counter) {
390 fprintf(stderrstderr, "%s ! out-of-sync write-counters\n", msg);
391 }
392 }
393
394 return (int)(rsp->result);
395}
396
397/* send an initialized rpmb request to the controller and read its response
398 * expected response size give in 'rsp_size'. returns response buffer upon
399 * successful completion (caller must free), NULL otherwise
400 */
401static struct rpmb_data_frame_t *
402rpmb_read_request(struct libnvme_transport_handle *hdl,
403 struct rpmb_data_frame_t *req,
404 int req_size,
405 int rsp_size)
406{
407 struct rpmb_data_frame_t *rsp = NULL((void*)0);
408 unsigned char msg[1024] = { 0 };
409 int error;
410
411 sprintf((char *)msg, "RPMB request 0x%04x to target 0x%x",
412 req->type, req->target);
413
414 error = send_rpmb_req(hdl, req->target, req_size, req);
415 if (error != 0) {
416 fprintf(stderrstderr, "%s failed with error = 0x%x\n",
417 msg, error);
418 goto error_out;
419 }
420
421 /* read the result back */
422 rsp = (struct rpmb_data_frame_t *)calloc(rsp_size, 1);
423 if (rsp == NULL((void*)0)) {
424 fprintf(stderrstderr, "memory alloc failed for %s\n", msg);
425 goto error_out;
426 }
427
428 /* Read result of previous request */
429 error = recv_rpmb_rsp(hdl, req->target, rsp_size, rsp);
430 if (error) {
431 fprintf(stderrstderr, "error 0x%x receiving response for %s\n",
432 error, msg);
433 goto error_out;
434 }
435
436 /* validate response buffer - match target, nonce, and mac */
437 error = check_rpmb_response(req, rsp, (char *)msg);
438 if (error == 0) return rsp;
439
440error_out:
441 free(rsp);
442 return NULL((void*)0);
443}
444
445/* read current write counter value from controller */
446static int rpmb_read_write_counter(struct libnvme_transport_handle *hdl,
447 unsigned char target,
448 unsigned int *counter)
449{
450 int error = -1;
451 int req_size = sizeof(struct rpmb_data_frame_t);
452 struct rpmb_data_frame_t *req = NULL((void*)0);
453 struct rpmb_data_frame_t *rsp = NULL((void*)0);
454
455 req = rpmb_request_init(req_size, RPMB_REQ_READ_WRITE_CNTR,
456 target, 1, 0, 0, NULL((void*)0), 0, 0);
457 if (req == NULL((void*)0)) goto out;
458 if ((rsp = rpmb_read_request(hdl, req, req_size, req_size)) == NULL((void*)0)) {
459 goto out;
460 }
461 *counter = rsp->write_counter;
462 error = 0;
463
464out:
465 free(req);
466 free(rsp);
467 return error;
468}
469
470/* Read current device configuration block into specified buffer. It also returns
471 * current write counter value returned as part of response, in case of error it
472 * returns 0
473 */
474static unsigned int rpmb_read_config_block(struct libnvme_transport_handle *hdl,
475 unsigned char **config_buf)
476{
477 int req_size = sizeof(struct rpmb_data_frame_t);
478 int cfg_size = sizeof(struct rpmb_config_block_t);
479 int rsp_size = req_size + cfg_size;
480
481 struct rpmb_data_frame_t *req = NULL((void*)0);
482 struct rpmb_data_frame_t *rsp = NULL((void*)0);
483 struct rpmb_config_block_t *cfg = NULL((void*)0);
484 unsigned int retval = 0;
485
486 /* initialize request with nonce, no data on input */
487 req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DCB_READ, 0, 1, 0, 1,
488 0, 0, 0);
489 if (!req
22.1
'req' is non-null
)
23
Taking false branch
490 return 0;
491 if ((rsp = rpmb_read_request(hdl, req, req_size, rsp_size)) == NULL((void*)0))
24
Taking false branch
492 {
493 free(req);
494 return 0;
495 }
496
497 /* copy configuration data to be sent back to caller */
498 cfg = (struct rpmb_config_block_t *)calloc(cfg_size, 1);
25
Memory is allocated
499 if (cfg == NULL((void*)0)) {
26
Assuming 'cfg' is not equal to NULL
27
Taking false branch
500 fprintf(stderrstderr, "failed to allocate RPMB config buffer\n");
501 goto out;
502 }
503
504 memcpy(cfg, rsp->data, cfg_size);
505 *config_buf = (unsigned char *)cfg;
506 cfg = NULL((void*)0);
507 retval = rsp->write_counter;
508out:
509 free(req);
510 free(rsp);
511 return retval;
512}
513
514
515static int rpmb_auth_data_read(struct libnvme_transport_handle *hdl,
516 unsigned char target,
517 unsigned int offset,
518 unsigned char **msg_buf,
519 int msg_size, int acc_size)
520{
521 struct rpmb_data_frame_t *req = NULL((void*)0);
522 struct rpmb_data_frame_t *rsp = NULL((void*)0);
523 int req_size = sizeof(struct rpmb_data_frame_t);
524 int chunk_size = (acc_size < msg_size) ? acc_size : msg_size;
525 int xfer = chunk_size;
526 unsigned char *bufp = (unsigned char *)malloc(msg_size * 512);
527 unsigned char *tbufp = bufp;
528 int data_size, rsp_size;
529 int error = -1;
530
531 if (bufp == NULL((void*)0)) {
532 fprintf(stderrstderr, "Failed to allocated memory for read-data req\n");
533 goto out;
534 }
535
536 while (xfer > 0) {
537 rsp_size = req_size + xfer * 512;
538 req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DATA_READ,
539 target, 1, offset, xfer, 0, 0, 0);
540 if (req == NULL((void*)0))
541 break;
542 if ((rsp = rpmb_read_request(hdl, req, req_size, rsp_size)) == NULL((void*)0))
543 {
544 fprintf(stderrstderr, "read_request failed\n");
545 free(req);
546 break;
547 }
548
549 data_size = rsp->sectors * 512;
550 memcpy(tbufp, rsp->data, data_size);
551 offset += rsp->sectors;
552 tbufp += data_size;
553 if (offset + chunk_size > msg_size)
554 xfer = msg_size - offset;
555 else
556 xfer = chunk_size;
557 free(req);
558 free(rsp);
559 }
560
561 *msg_buf = bufp;
562 error = offset;
563out:
564 return error;
565}
566
567/* Implementation of programming authentication key to given RPMB target */
568static int rpmb_program_auth_key(struct libnvme_transport_handle *hdl,
569 unsigned char target, unsigned char *key_buf,
570 int key_size)
571{
572 int req_size = sizeof(struct rpmb_data_frame_t);
573 int rsp_size = sizeof(struct rpmb_data_frame_t);
574
575 struct rpmb_data_frame_t *req = NULL((void*)0);
576 struct rpmb_data_frame_t *rsp = NULL((void*)0);
577
578 int err = -ENOMEM12;
579
580 req = rpmb_request_init(req_size, RPMB_REQ_AUTH_KEY_PROGRAM, target,
581 0, 0, 0, key_buf, (223 - key_size), key_size);
582 if (req == NULL((void*)0)) {
583 fprintf(stderrstderr, "failed to allocate request buffer memory\n");
584 goto out;
585 }
586
587 /* send the request and get response */
588 err = send_rpmb_req(hdl, req->target, req_size, req);
589 if (err) {
590 fprintf(stderrstderr, "RPMB request 0x%04x for 0x%x, err: %d\n", req->type, req->target,
591 err);
592 goto out;
593 }
594
595 /* send the request to get the result and then request to get the response */
596 rsp = (struct rpmb_data_frame_t *)calloc(rsp_size, 1);
597 if (!rsp) {
598 fprintf(stderrstderr, "failed to allocate response buffer memory\n");
599 err = -ENOMEM12;
600 goto out;
601 }
602
603 rsp->target = req->target;
604 rsp->type = RPMB_REQ_READ_RESULT;
605 err = send_rpmb_req(hdl, req->target, rsp_size, rsp);
606 if (err || rsp->result) {
607 fprintf(stderrstderr, "Program auth key read result 0x%x, error = 0x%x\n", rsp->result,
608 err);
609 goto out;
610 }
611
612 /* reuse response buffer */
613 memset(rsp, 0, rsp_size);
614 err = recv_rpmb_rsp(hdl, req->target, rsp_size, rsp);
615 if (err != 0)
616 fprintf(stderrstderr, "Program Key recv error = 0x%x\n", err);
617 else
618 err = check_rpmb_response(req, rsp, "Failed to Program Key");
619out:
620 free(req);
621 free(rsp);
622
623 return err;
624}
625
626
627/* Implementation of RPMB authenticated data write command; this function
628 * transfers msg_size bytes from msg_buf to controller 'addr'. Returns
629 * number of bytes actually written to, otherwise negetive error code
630 * on failures.
631 */
632static int auth_data_write_chunk(struct libnvme_transport_handle *hdl,
633 unsigned char tgt, unsigned int addr,
634 unsigned char *msg_buf, int msg_size,
635 unsigned char *keybuf, int keysize)
636{
637 int req_size = sizeof(struct rpmb_data_frame_t) + msg_size;
638 int rsp_size = sizeof(struct rpmb_data_frame_t);
639
640 struct rpmb_data_frame_t *req = NULL((void*)0);
641 struct rpmb_data_frame_t *rsp = NULL((void*)0);
642
643 unsigned int write_cntr = 0;
644 unsigned char *mac = NULL((void*)0);
645 int error = -ENOMEM12;
646
647 /* get current write counter and copy to the request */
648 error = rpmb_read_write_counter(hdl, tgt, &write_cntr);
649 if (error != 0) {
650 fprintf(stderrstderr, "Failed to read write counter for write-data\n");
651 goto out;
652 }
653
654 req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DATA_WRITE, tgt, 0,
655 addr, (msg_size / 512), msg_buf,
656 offsetof(struct rpmb_data_frame_t, data)__builtin_offsetof(struct rpmb_data_frame_t, data), msg_size);
657 if (req == NULL((void*)0)) {
658 fprintf(stderrstderr, "Memory alloc failed for write-data command\n");
659 goto out;
660 }
661
662 req->write_counter = write_cntr;
663
664 /* compute HMAC hash */
665 mac = hmac_sha256(((unsigned char *)req + 223), req_size - 223,
666 keybuf, keysize);
667 if (mac == NULL((void*)0)) {
668 fprintf(stderrstderr, "failed to compute HMAC-SHA256\n");
669 error = -1;
670 goto out;
671 }
672
673 memcpy(req->mac, mac, 32);
674
675 /* send the request and get response */
676 error = send_rpmb_req(hdl, tgt, req_size, req);
677 if (error != 0) {
678 fprintf(stderrstderr, "RPMB request 0x%04x for 0x%x, error: %d\n",
679 req->type, tgt, error);
680 goto out;
681 }
682
683 /* send the request to get the result and then request to get the response */
684 rsp = (struct rpmb_data_frame_t *)calloc(rsp_size, 1);
685 rsp->target = req->target;
686 rsp->type = RPMB_REQ_READ_RESULT;
687 error = send_rpmb_req(hdl, tgt, rsp_size, rsp);
688 if (error != 0 || rsp->result != 0) {
689 fprintf(stderrstderr, "Write-data read result 0x%x, error = 0x%x\n",
690 rsp->result, error);
691 goto out;
692 }
693
694 /* Read final response */
695 memset(rsp, 0, rsp_size);
696 error = recv_rpmb_rsp(hdl, tgt, rsp_size, rsp);
697 if (error != 0)
698 fprintf(stderrstderr, "Auth data write recv error = 0x%x\n", error);
699 else
700 error = check_rpmb_response(req, rsp, "Failed to write-data");
701out:
702 free(req);
703 free(rsp);
704 free(mac);
705
706 return error;
707}
708
709/* send the request and get response */
710static int rpmb_auth_data_write(struct libnvme_transport_handle *hdl,
711 unsigned char target, unsigned int addr,
712 int acc_size, unsigned char *msg_buf,
713 int msg_size, unsigned char *keybuf,
714 int keysize)
715{
716 int chunk_size = acc_size < msg_size ? acc_size : msg_size;
717 int xfer = chunk_size;
718 int offset = 0;
719
720 while (xfer > 0 ) {
721 if (auth_data_write_chunk(hdl, target, (addr + offset / 512),
722 msg_buf + offset, xfer,
723 keybuf, keysize) != 0)
724 {
725 /* error writing chunk data */
726 break;
727 }
728
729 offset += xfer;
730 if (offset + chunk_size > msg_size)
731 xfer = msg_size - offset;
732 else
733 xfer = chunk_size;
734 }
735
736 return offset;
737}
738
739/* writes given config_block buffer to the drive target 0 */
740static int rpmb_write_config_block(struct libnvme_transport_handle *hdl,
741 unsigned char *cfg_buf,
742 unsigned char *keybuf, int keysize)
743{
744 int cfg_size = sizeof(struct rpmb_config_block_t);
745 int rsp_size = sizeof(struct rpmb_data_frame_t);
746 int req_size = rsp_size + cfg_size;
747
748 struct rpmb_data_frame_t *req = NULL((void*)0);
749 struct rpmb_data_frame_t *rsp = NULL((void*)0);
750 unsigned char *cfg_buf_read = NULL((void*)0), *mac = NULL((void*)0);
751 unsigned int write_cntr = 0;
752 int error = -ENOMEM12;
753
754 /* initialize request */
755 req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DCB_WRITE, 0, 0, 0, 1,
756 cfg_buf, offsetof(struct rpmb_data_frame_t, data)__builtin_offsetof(struct rpmb_data_frame_t, data),
757 cfg_size);
758 if (req == NULL((void*)0)) {
759 fprintf(stderrstderr, "failed to allocate rpmb request buffer\n");
760 goto out;
761 }
762
763 /* read config block write_counter from controller */
764 write_cntr = rpmb_read_config_block(hdl, &cfg_buf_read);
765 if (cfg_buf_read == NULL((void*)0)) {
766 fprintf(stderrstderr, "failed to read config block write counter\n");
767 error = -EIO5;
768 goto out;
769 }
770
771 free(cfg_buf_read);
772 req->write_counter = write_cntr;
773 mac = hmac_sha256(((unsigned char *)req + 223), req_size - 223,
774 keybuf, keysize);
775 if (mac == NULL((void*)0)) {
776 fprintf(stderrstderr, "failed to compute hmac-sha256 hash\n");
777 error = -EINVAL22;
778 goto out;
779 }
780
781 memcpy(req->mac, mac, sizeof(req->mac));
782
783 error = send_rpmb_req(hdl, 0, req_size, req);
784 if (error != 0) {
785 fprintf(stderrstderr, "Write-config RPMB request, error = 0x%x\n",
786 error);
787 goto out;
788 }
789
790 /* get response */
791 rsp = (struct rpmb_data_frame_t *)calloc(rsp_size, 1);
792 if (rsp == NULL((void*)0)) {
793 fprintf(stderrstderr, "failed to allocate response buffer memory\n");
794 error = -ENOMEM12;
795 goto out;
796 }
797
798 /* get result first */
799 memset(rsp, 0, rsp_size);
800 rsp->target = req->target;
801 rsp->type = RPMB_REQ_READ_RESULT;
802 /* get the response and validate */
803 error = recv_rpmb_rsp(hdl, req->target, rsp_size, rsp);
804 if (error != 0) {
805 fprintf(stderrstderr,"Failed getting write-config response\
806 error = 0x%x\n", error);
807 goto out;
808 }
809 error = check_rpmb_response(req, rsp,
810 "Failed to retrieve write-config response");
811out:
812 free(req);
813 free(rsp);
814 free(mac);
815
816 return error;
817}
818
819static bool_Bool invalid_xfer_size(int blocks, unsigned int bpsz)
820{
821 return ((blocks <= 0) ||
822 (blocks * 512) > ((bpsz + 1) * 128 * 1024));
823}
824
825/* Handling rpmb sub-command */
826int rpmb_cmd_option(int argc, char **argv, struct command *acmd, struct plugin *plugin)
827{
828 const char *desc = "Run RPMB command on the supporting controller";
829 const char *msg = "data to be written on write-data or write-config commands";
830 const char *mfile = "data file for read/write-data, read/write-config options";
831 const char *kfile = "key file that has authentication key to be used";
832 const char *target = "RPMB target - numerical value of 0 to 6, default 0";
833 const char *address = "Sector offset to read from or write to for an RPMB target, default 0";
834 const char *blocks = "Number of 512 blocks to read or write";
835 const char *key = "key to be used for authentication";
836 const char *opt = "RPMB action - info, program-key, read-counter, write-data, " \
837 "read-data, write-config and read-config";
838
839 struct config {
840 char *cmd;
841 char *key;
842 char *msg;
843 char *keyfile;
844 char *msgfile;
845 int opt;
846 int address;
847 int blocks;
848 char target;
849 };
850
851 struct config cfg = {
852 .cmd = "info",
853 .key = NULL((void*)0),
854 .msg = NULL((void*)0),
855 .msgfile = NULL((void*)0),
856 .keyfile = NULL((void*)0),
857 .opt = 0,
858 .address = 0,
859 .blocks = 0,
860 .target = 0,
861 };
862
863 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"cmd", 'c', "command", CFG_STRING, &cfg.cmd, 1, opt
, 0, }, {"msgfile", 'f', "FILE", CFG_STRING, &cfg.msgfile
, 1, mfile, 0, }, {"keyfile", 'g', "FILE", CFG_STRING, &cfg
.keyfile, 1, kfile, 0, }, {"key", 'k', "key", CFG_STRING, &
cfg.key, 1, key, 0, }, {"msg", 'd', "data", CFG_STRING, &
cfg.msg, 1, msg, 0, }, {"address", 'o', "NUM", CFG_POSITIVE, &
cfg.address, 1, address, 0, }, {"blocks", 'b', "NUM", CFG_POSITIVE
, &cfg.blocks, 1, blocks, 0, }, {"target", 't', "NUM", CFG_POSITIVE
, &cfg.target, 1, target, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
864 OPT_STRING("cmd", 'c', "command", &cfg.cmd, opt),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"cmd", 'c', "command", CFG_STRING, &cfg.cmd, 1, opt
, 0, }, {"msgfile", 'f', "FILE", CFG_STRING, &cfg.msgfile
, 1, mfile, 0, }, {"keyfile", 'g', "FILE", CFG_STRING, &cfg
.keyfile, 1, kfile, 0, }, {"key", 'k', "key", CFG_STRING, &
cfg.key, 1, key, 0, }, {"msg", 'd', "data", CFG_STRING, &
cfg.msg, 1, msg, 0, }, {"address", 'o', "NUM", CFG_POSITIVE, &
cfg.address, 1, address, 0, }, {"blocks", 'b', "NUM", CFG_POSITIVE
, &cfg.blocks, 1, blocks, 0, }, {"target", 't', "NUM", CFG_POSITIVE
, &cfg.target, 1, target, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
865 OPT_STRING("msgfile", 'f', "FILE", &cfg.msgfile, mfile),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"cmd", 'c', "command", CFG_STRING, &cfg.cmd, 1, opt
, 0, }, {"msgfile", 'f', "FILE", CFG_STRING, &cfg.msgfile
, 1, mfile, 0, }, {"keyfile", 'g', "FILE", CFG_STRING, &cfg
.keyfile, 1, kfile, 0, }, {"key", 'k', "key", CFG_STRING, &
cfg.key, 1, key, 0, }, {"msg", 'd', "data", CFG_STRING, &
cfg.msg, 1, msg, 0, }, {"address", 'o', "NUM", CFG_POSITIVE, &
cfg.address, 1, address, 0, }, {"blocks", 'b', "NUM", CFG_POSITIVE
, &cfg.blocks, 1, blocks, 0, }, {"target", 't', "NUM", CFG_POSITIVE
, &cfg.target, 1, target, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
866 OPT_STRING("keyfile", 'g', "FILE", &cfg.keyfile, kfile),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"cmd", 'c', "command", CFG_STRING, &cfg.cmd, 1, opt
, 0, }, {"msgfile", 'f', "FILE", CFG_STRING, &cfg.msgfile
, 1, mfile, 0, }, {"keyfile", 'g', "FILE", CFG_STRING, &cfg
.keyfile, 1, kfile, 0, }, {"key", 'k', "key", CFG_STRING, &
cfg.key, 1, key, 0, }, {"msg", 'd', "data", CFG_STRING, &
cfg.msg, 1, msg, 0, }, {"address", 'o', "NUM", CFG_POSITIVE, &
cfg.address, 1, address, 0, }, {"blocks", 'b', "NUM", CFG_POSITIVE
, &cfg.blocks, 1, blocks, 0, }, {"target", 't', "NUM", CFG_POSITIVE
, &cfg.target, 1, target, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
867 OPT_STRING("key", 'k', "key", &cfg.key, key),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"cmd", 'c', "command", CFG_STRING, &cfg.cmd, 1, opt
, 0, }, {"msgfile", 'f', "FILE", CFG_STRING, &cfg.msgfile
, 1, mfile, 0, }, {"keyfile", 'g', "FILE", CFG_STRING, &cfg
.keyfile, 1, kfile, 0, }, {"key", 'k', "key", CFG_STRING, &
cfg.key, 1, key, 0, }, {"msg", 'd', "data", CFG_STRING, &
cfg.msg, 1, msg, 0, }, {"address", 'o', "NUM", CFG_POSITIVE, &
cfg.address, 1, address, 0, }, {"blocks", 'b', "NUM", CFG_POSITIVE
, &cfg.blocks, 1, blocks, 0, }, {"target", 't', "NUM", CFG_POSITIVE
, &cfg.target, 1, target, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
868 OPT_STRING("msg", 'd', "data", &cfg.msg, msg),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"cmd", 'c', "command", CFG_STRING, &cfg.cmd, 1, opt
, 0, }, {"msgfile", 'f', "FILE", CFG_STRING, &cfg.msgfile
, 1, mfile, 0, }, {"keyfile", 'g', "FILE", CFG_STRING, &cfg
.keyfile, 1, kfile, 0, }, {"key", 'k', "key", CFG_STRING, &
cfg.key, 1, key, 0, }, {"msg", 'd', "data", CFG_STRING, &
cfg.msg, 1, msg, 0, }, {"address", 'o', "NUM", CFG_POSITIVE, &
cfg.address, 1, address, 0, }, {"blocks", 'b', "NUM", CFG_POSITIVE
, &cfg.blocks, 1, blocks, 0, }, {"target", 't', "NUM", CFG_POSITIVE
, &cfg.target, 1, target, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
869 OPT_UINT("address", 'o', &cfg.address, address),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"cmd", 'c', "command", CFG_STRING, &cfg.cmd, 1, opt
, 0, }, {"msgfile", 'f', "FILE", CFG_STRING, &cfg.msgfile
, 1, mfile, 0, }, {"keyfile", 'g', "FILE", CFG_STRING, &cfg
.keyfile, 1, kfile, 0, }, {"key", 'k', "key", CFG_STRING, &
cfg.key, 1, key, 0, }, {"msg", 'd', "data", CFG_STRING, &
cfg.msg, 1, msg, 0, }, {"address", 'o', "NUM", CFG_POSITIVE, &
cfg.address, 1, address, 0, }, {"blocks", 'b', "NUM", CFG_POSITIVE
, &cfg.blocks, 1, blocks, 0, }, {"target", 't', "NUM", CFG_POSITIVE
, &cfg.target, 1, target, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
870 OPT_UINT("blocks", 'b', &cfg.blocks, blocks),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"cmd", 'c', "command", CFG_STRING, &cfg.cmd, 1, opt
, 0, }, {"msgfile", 'f', "FILE", CFG_STRING, &cfg.msgfile
, 1, mfile, 0, }, {"keyfile", 'g', "FILE", CFG_STRING, &cfg
.keyfile, 1, kfile, 0, }, {"key", 'k', "key", CFG_STRING, &
cfg.key, 1, key, 0, }, {"msg", 'd', "data", CFG_STRING, &
cfg.msg, 1, msg, 0, }, {"address", 'o', "NUM", CFG_POSITIVE, &
cfg.address, 1, address, 0, }, {"blocks", 'b', "NUM", CFG_POSITIVE
, &cfg.blocks, 1, blocks, 0, }, {"target", 't', "NUM", CFG_POSITIVE
, &cfg.target, 1, target, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
871 OPT_UINT("target", 't', &cfg.target, target))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"cmd", 'c', "command", CFG_STRING, &cfg.cmd, 1, opt
, 0, }, {"msgfile", 'f', "FILE", CFG_STRING, &cfg.msgfile
, 1, mfile, 0, }, {"keyfile", 'g', "FILE", CFG_STRING, &cfg
.keyfile, 1, kfile, 0, }, {"key", 'k', "key", CFG_STRING, &
cfg.key, 1, key, 0, }, {"msg", 'd', "data", CFG_STRING, &
cfg.msg, 1, msg, 0, }, {"address", 'o', "NUM", CFG_POSITIVE, &
cfg.address, 1, address, 0, }, {"blocks", 'b', "NUM", CFG_POSITIVE
, &cfg.blocks, 1, blocks, 0, }, {"target", 't', "NUM", CFG_POSITIVE
, &cfg.target, 1, target, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
;
872
873 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) unsigned char *key_buf = NULL((void*)0);
874 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) unsigned char *msg_buf = NULL((void*)0);
875 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
876 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
877 unsigned int write_cntr = 0;
878 unsigned int msg_size = 0;
879 unsigned int key_size = 0;
880 struct nvme_id_ctrl ctrl;
881 int err = -1;
882
883 union ctrl_rpmbs_reg {
884 struct {
885 unsigned int num_targets:3;
886 unsigned int auth_method:3;
887 unsigned int reserved:10;
888 unsigned int total_size:8; /* 128K units */
889 unsigned int access_size:8; /* in 512 byte count */
890 };
891 unsigned int rpmbs;
892 } regs;
893
894 if ((err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts)))
1
Assuming 'err' is 0
2
Taking false branch
895 return err;
896
897 /* before parsing commands, check if controller supports any RPMB targets */
898 err = nvme_identify_ctrl(hdl, &ctrl);
899 if (err)
3
Assuming 'err' is 0
4
Taking false branch
900 return err;
901
902 regs.rpmbs = le32_to_cpu(ctrl.rpmbs);
903 if (regs.num_targets == 0) {
5
Assuming field 'num_targets' is not equal to 0
904 fprintf(stderrstderr, "No RPMB targets are supported by the drive\n");
905 return -1;
906 }
907
908 /* parse and validate options; default print rpmb support info */
909 if (cfg.cmd == 0 || strcmp(cfg.cmd, "info") == 0) {
6
Assuming field 'cmd' is not equal to null
7
Assuming the condition is false
8
Taking false branch
910 nvme_show_id_ctrl_rpmbs(regs.rpmbs, 0);
911 return -1;
912 }
913
914 if (strcmp(cfg.cmd, "program-key") == 0)
9
Assuming the condition is false
10
Taking false branch
915 cfg.opt = RPMB_REQ_AUTH_KEY_PROGRAM;
916 else if (strcmp(cfg.cmd, "read-counter") == 0)
11
Assuming the condition is false
12
Taking false branch
917 cfg.opt = RPMB_REQ_READ_WRITE_CNTR;
918 else if (strcmp(cfg.cmd, "write-data") == 0)
13
Assuming the condition is false
14
Taking false branch
919 cfg.opt = RPMB_REQ_AUTH_DATA_WRITE;
920 else if (strcmp(cfg.cmd, "read-data") == 0)
15
Assuming the condition is false
16
Taking false branch
921 cfg.opt = RPMB_REQ_AUTH_DATA_READ;
922 else if (strcmp(cfg.cmd, "write-config") == 0)
17
Assuming the condition is false
18
Taking false branch
923 cfg.opt = RPMB_REQ_AUTH_DCB_WRITE;
924 else if (strcmp(cfg.cmd, "read-config") == 0)
19
Taking true branch
925 cfg.opt = RPMB_REQ_AUTH_DCB_READ;
926 else {
927 fprintf(stderrstderr, "Invalid option %s for rpmb command\n", cfg.cmd);
928 return -1;
929 }
930
931 /* input file/data processing */
932 if (cfg.opt
19.1
Field 'opt' is not equal to RPMB_REQ_AUTH_DCB_WRITE
== RPMB_REQ_AUTH_DCB_WRITE ||
20
Taking false branch
933 cfg.opt
19.2
Field 'opt' is not equal to RPMB_REQ_AUTH_DATA_WRITE
== RPMB_REQ_AUTH_DATA_WRITE ||
934 cfg.opt
19.3
Field 'opt' is not equal to RPMB_REQ_AUTH_KEY_PROGRAM
== RPMB_REQ_AUTH_KEY_PROGRAM)
935 {
936 key_buf = read_rpmb_key(cfg.key, cfg.keyfile, &key_size);
937 if (key_buf == NULL((void*)0)) {
938 fprintf(stderrstderr, "Failed to read key\n");
939 return -1;
940 }
941
942 if (key_size > 223 || key_size <= 0) {
943 fprintf(stderrstderr, "Invalid key size %d, valid input 1 to 223\n",
944 key_size);
945 return -1;
946 }
947
948 if (cfg.opt == RPMB_REQ_AUTH_DCB_WRITE ||
949 cfg.opt == RPMB_REQ_AUTH_DATA_WRITE) {
950 if (cfg.msg != NULL((void*)0)) {
951 msg_size = strlen(cfg.msg);
952 msg_buf = libnvme_alloc(msg_size);
953 memcpy(msg_buf, cfg.msg, msg_size);
954 } else {
955 err = read_file(cfg.msgfile, &msg_buf, &msg_size);
956 if (err || msg_size <= 0) {
957 fprintf(stderrstderr, "Failed to read file %s\n",
958 cfg.msgfile);
959 return -1;
960 }
961 }
962 }
963 }
964
965 switch (cfg.opt) {
21
Control jumps to 'case RPMB_REQ_AUTH_DCB_READ:' at line 972
966 case RPMB_REQ_READ_WRITE_CNTR:
967 err = rpmb_read_write_counter(hdl, cfg.target, &write_cntr);
968 if (err == 0)
969 printf("Write Counter is: %u\n", write_cntr);
970 break;
971
972 case RPMB_REQ_AUTH_DCB_READ:
973 write_cntr = rpmb_read_config_block(hdl, &msg_buf);
22
Calling 'rpmb_read_config_block'
28
Returned allocated memory via 2nd parameter
974 if (msg_buf
28.1
'msg_buf' is not equal to NULL
== NULL((void*)0)) {
29
Taking false branch
975 fprintf(stderrstderr, "failed read config blk\n");
976 return -1;
977 }
978
979 /* no output file is given, print the data on stdout */
980 if (cfg.msgfile == 0) {
30
Assuming field 'msgfile' is equal to null
31
Taking true branch
981 struct rpmb_config_block_t *cfg =
982 (struct rpmb_config_block_t *)msg_buf;
983 printf("Boot Partition Protection is %s\n",
984 ((cfg->bp_enable & 0x1) ? "Enabled" : "Disabled"));
32
Assuming the condition is false
33
'?' condition is false
985 printf("Boot Partition 1 is %s\n",
986 ((cfg->bp_lock & 0x2) ? "Locked" : "Unlocked"));
34
Assuming the condition is false
35
'?' condition is false
987 printf("Boot Partition 0 is %s\n",
988 ((cfg->bp_lock & 0x1) ? "Locked" : "Unlocked"));
36
Assuming the condition is false
37
'?' condition is false
38
Potential leak of memory pointed to by 'cfg'
989 } else {
990 printf("Saving received config data to %s file\n", cfg.msgfile);
991 write_file(msg_buf, sizeof(struct rpmb_config_block_t), NULL((void*)0),
992 cfg.msgfile, NULL((void*)0));
993 }
994 err = (write_cntr == 0);
995 break;
996
997 case RPMB_REQ_AUTH_DATA_READ:
998 /* check if requested data is beyond what target supports */
999 msg_size = cfg.blocks * 512;
1000 if (invalid_xfer_size(cfg.blocks, regs.total_size)) {
1001 fprintf(stderrstderr, "invalid transfer size %d \n",
1002 msg_size);
1003 break;
1004 }
1005 err = rpmb_auth_data_read(hdl, cfg.target,
1006 cfg.address, &msg_buf,
1007 cfg.blocks,
1008 (regs.access_size + 1));
1009 if (err > 0 && msg_buf != NULL((void*)0)) {
1010 printf("Writing %d bytes to file %s\n",
1011 err * 512, cfg.msgfile);
1012 write_file(msg_buf, err * 512, NULL((void*)0),
1013 cfg.msgfile, NULL((void*)0));
1014 }
1015 break;
1016
1017 case RPMB_REQ_AUTH_DATA_WRITE:
1018 if (invalid_xfer_size(cfg.blocks, regs.total_size) ||
1019 (cfg.blocks * 512) > msg_size) {
1020 fprintf(stderrstderr, "invalid transfer size %d\n",
1021 cfg.blocks * 512);
1022 break;
1023 } else if ((cfg.blocks * 512) < msg_size) {
1024 msg_size = cfg.blocks * 512;
1025 }
1026 err = rpmb_auth_data_write(hdl, cfg.target,
1027 cfg.address,
1028 ((regs.access_size + 1) * 512),
1029 msg_buf, msg_size,
1030 key_buf, key_size);
1031
1032 /* print whatever extent of data written to target */
1033 printf("Written %d sectors out of %d @target(%d):0x%x\n",
1034 err/512, msg_size/512, cfg.target, cfg.address);
1035 break;
1036
1037 case RPMB_REQ_AUTH_DCB_WRITE:
1038 err = rpmb_write_config_block(hdl, msg_buf,
1039 key_buf, key_size);
1040 break;
1041
1042 case RPMB_REQ_AUTH_KEY_PROGRAM:
1043 err = rpmb_program_auth_key(hdl, cfg.target,
1044 key_buf, key_size);
1045 break;
1046 default:
1047 break;
1048 }
1049
1050 return err;
1051}