(root)/
strace-6.5/
src/
dm.c
       1  /*
       2   * Support for decoding of DM_* ioctl commands.
       3   *
       4   * Copyright (c) 2016 Mikulas Patocka <mpatocka@redhat.com>
       5   * Copyright (c) 2016 Masatake Yamato <yamato@redhat.com>
       6   * Copyright (c) 2016 Dmitry V. Levin <ldv@strace.io>
       7   * Copyright (c) 2016 Eugene Syromyatnikov <evgsyr@gmail.com>
       8   * Copyright (c) 2016-2022 The strace developers.
       9   * All rights reserved.
      10   *
      11   * SPDX-License-Identifier: LGPL-2.1-or-later
      12   */
      13  
      14  #include "defs.h"
      15  #include <linux/dm-ioctl.h>
      16  #include <linux/ioctl.h>
      17  
      18  /* Defined in lvm2/libdm/ioctl/libdm-iface.c file.  */
      19  #define DM_EXISTS_FLAG 0x00000004
      20  
      21  #include "xlat/dm_flags.h"
      22  
      23  static void
      24  dm_decode_device(const unsigned int code, const struct dm_ioctl *ioc)
      25  {
      26  	switch (code) {
      27  	case DM_REMOVE_ALL:
      28  	case DM_LIST_DEVICES:
      29  	case DM_LIST_VERSIONS:
      30  		break;
      31  	default:
      32  		if (ioc->dev) {
      33  			tprint_struct_next();
      34  			PRINT_FIELD_DEV(*ioc, dev);
      35  		}
      36  
      37  		if (ioc->name[0]) {
      38  			tprint_struct_next();
      39  			PRINT_FIELD_CSTRING(*ioc, name);
      40  		}
      41  
      42  		if (ioc->uuid[0]) {
      43  			tprint_struct_next();
      44  			PRINT_FIELD_CSTRING(*ioc, uuid);
      45  		}
      46  
      47  		break;
      48  	}
      49  }
      50  
      51  static void
      52  dm_decode_values(struct tcb *tcp, const unsigned int code,
      53  		 const struct dm_ioctl *ioc)
      54  {
      55  	if (entering(tcp)) {
      56  		switch (code) {
      57  		case DM_TABLE_LOAD:
      58  			tprint_struct_next();
      59  			PRINT_FIELD_U(*ioc, target_count);
      60  			break;
      61  		case DM_DEV_SUSPEND:
      62  			if (ioc->flags & DM_SUSPEND_FLAG)
      63  				break;
      64  			ATTRIBUTE_FALLTHROUGH;
      65  		case DM_DEV_RENAME:
      66  		case DM_DEV_REMOVE:
      67  		case DM_DEV_WAIT:
      68  			tprint_struct_next();
      69  			PRINT_FIELD_U(*ioc, event_nr);
      70  			break;
      71  		}
      72  	} else if (!syserror(tcp)) {
      73  		switch (code) {
      74  		case DM_DEV_CREATE:
      75  		case DM_DEV_RENAME:
      76  		case DM_DEV_SUSPEND:
      77  		case DM_DEV_STATUS:
      78  		case DM_DEV_WAIT:
      79  		case DM_TABLE_LOAD:
      80  		case DM_TABLE_CLEAR:
      81  		case DM_TABLE_DEPS:
      82  		case DM_TABLE_STATUS:
      83  		case DM_TARGET_MSG:
      84  			tprint_struct_next();
      85  			PRINT_FIELD_U(*ioc, target_count);
      86  			tprint_struct_next();
      87  			PRINT_FIELD_U(*ioc, open_count);
      88  			tprint_struct_next();
      89  			PRINT_FIELD_U(*ioc, event_nr);
      90  			break;
      91  		}
      92  	}
      93  }
      94  
      95  static void
      96  dm_decode_flags(const struct dm_ioctl *ioc)
      97  {
      98  	tprint_struct_next();
      99  	PRINT_FIELD_FLAGS(*ioc, flags, dm_flags, "DM_???");
     100  }
     101  
     102  static bool
     103  dm_ioctl_has_params(const unsigned int code)
     104  {
     105  	switch (code) {
     106  	case DM_VERSION:
     107  	case DM_REMOVE_ALL:
     108  	case DM_DEV_CREATE:
     109  	case DM_DEV_REMOVE:
     110  	case DM_DEV_SUSPEND:
     111  	case DM_DEV_STATUS:
     112  	case DM_TABLE_CLEAR:
     113  	case DM_DEV_ARM_POLL:
     114  		return false;
     115  	}
     116  
     117  	return true;
     118  }
     119  
     120  static bool
     121  dm_decode_header(struct tcb *const tcp, const unsigned int code,
     122  		 const kernel_ulong_t arg, const struct dm_ioctl *const ioc)
     123  {
     124  	bool rc = false;
     125  
     126  	tprint_struct_begin();
     127  	/*
     128  	 * device mapper code uses %d in some places and %u in another, but
     129  	 * fields themselves are declared as __u32.
     130  	 */
     131  	PRINT_FIELD_U_ARRAY(*ioc, version);
     132  	/*
     133  	 * if we use a different version of ABI, do not attempt to decode
     134  	 * ioctl fields
     135  	 */
     136  	if (ioc->version[0] != DM_VERSION_MAJOR) {
     137  		tprints_comment("unsupported device mapper ABI version");
     138  		goto skip;
     139  	}
     140  
     141  	tprint_struct_next();
     142  	PRINT_FIELD_U(*ioc, data_size);
     143  
     144  	if (ioc->data_size < offsetof(struct dm_ioctl, data)) {
     145  		tprints_comment("data_size too small");
     146  		goto skip;
     147  	}
     148  
     149  	if (dm_ioctl_has_params(code)) {
     150  		tprint_struct_next();
     151  		PRINT_FIELD_U(*ioc, data_start);
     152  	}
     153  
     154  	dm_decode_device(code, ioc);
     155  	dm_decode_values(tcp, code, ioc);
     156  	dm_decode_flags(ioc);
     157  	rc = true;
     158  
     159   skip:
     160  	tprint_struct_end();
     161  	return rc;
     162  }
     163  
     164  static void
     165  dm_decode_dm_target_spec(struct tcb *const tcp, const kernel_ulong_t addr,
     166  			 const struct dm_ioctl *const ioc)
     167  {
     168  	static const uint32_t target_spec_size =
     169  		sizeof(struct dm_target_spec);
     170  	uint32_t offset = ioc->data_start;
     171  	uint32_t offset_end = 0;
     172  
     173  	if (abbrev(tcp)) {
     174  		if (ioc->target_count) {
     175  			tprint_array_next();
     176  			tprint_more_data_follows();
     177  		}
     178  
     179  		return;
     180  	}
     181  
     182  	for (uint32_t i = 0; i < ioc->target_count; ++i) {
     183  		tprint_array_next();
     184  
     185  		if (i && offset <= offset_end)
     186  			goto misplaced;
     187  
     188  		offset_end = offset + target_spec_size;
     189  
     190  		if (offset_end <= offset || offset_end > ioc->data_size)
     191  			goto misplaced;
     192  
     193  		if (i >= max_strlen) {
     194  			tprint_more_data_follows();
     195  			break;
     196  		}
     197  
     198  		struct dm_target_spec s;
     199  
     200  		if (umove_or_printaddr(tcp, addr + offset, &s))
     201  			break;
     202  
     203  		tprint_struct_begin();
     204  		PRINT_FIELD_U(s, sector_start);
     205  		tprint_struct_next();
     206  		PRINT_FIELD_U(s, length);
     207  
     208  		if (exiting(tcp)) {
     209  			tprint_struct_next();
     210  			PRINT_FIELD_D(s, status);
     211  		}
     212  
     213  		tprint_struct_next();
     214  		PRINT_FIELD_CSTRING(s, target_type);
     215  
     216  		tprint_struct_next();
     217  		tprints_field_name("string");
     218  		printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
     219  			     QUOTE_0_TERMINATED);
     220  		tprint_struct_end();
     221  
     222  		if (entering(tcp))
     223  			offset += s.next;
     224  		else
     225  			offset = ioc->data_start + s.next;
     226  	}
     227  
     228  	return;
     229  
     230  misplaced:
     231  	tprint_unavailable();
     232  	tprints_comment("misplaced struct dm_target_spec");
     233  }
     234  
     235  static bool
     236  dm_print_dev(struct tcb *tcp, void *dev_ptr, size_t dev_size, void *dummy)
     237  {
     238  	uint64_t *dev = (uint64_t *) dev_ptr;
     239  
     240  	print_dev_t(*dev);
     241  
     242  	return 1;
     243  }
     244  
     245  static void
     246  dm_decode_dm_target_deps(struct tcb *const tcp, const kernel_ulong_t addr,
     247  			 const struct dm_ioctl *const ioc)
     248  {
     249  	if (ioc->data_start == ioc->data_size)
     250  		return;
     251  
     252  	tprint_array_next();
     253  
     254  	if (abbrev(tcp)) {
     255  		tprint_more_data_follows();
     256  		return;
     257  	}
     258  
     259  	static const uint32_t target_deps_dev_offs =
     260  		offsetof(struct dm_target_deps, dev);
     261  	uint64_t dev_buf;
     262  	struct dm_target_deps s;
     263  	uint32_t offset = ioc->data_start;
     264  	uint32_t offset_end = offset + target_deps_dev_offs;
     265  	uint32_t space;
     266  
     267  	if (offset_end <= offset || offset_end > ioc->data_size)
     268  		goto misplaced;
     269  
     270  	if (umove_or_printaddr(tcp, addr + offset, &s))
     271  		return;
     272  
     273  	space = (ioc->data_size - offset_end) / sizeof(dev_buf);
     274  
     275  	if (s.count > space)
     276  		goto misplaced;
     277  
     278  	tprint_struct_begin();
     279  	PRINT_FIELD_U(s, count);
     280  
     281  	tprint_struct_next();
     282  	tprints_field_name("deps");
     283  	print_array(tcp, addr + offset_end, s.count, &dev_buf, sizeof(dev_buf),
     284  		    tfetch_mem, dm_print_dev, NULL);
     285  
     286  	tprint_struct_end();
     287  
     288  	return;
     289  
     290  misplaced:
     291  	tprint_unavailable();
     292  	tprints_comment("misplaced struct dm_target_deps");
     293  }
     294  
     295  static void
     296  dm_decode_dm_name_list(struct tcb *const tcp, const kernel_ulong_t addr,
     297  		       const struct dm_ioctl *const ioc)
     298  {
     299  	static const uint32_t name_list_name_offs =
     300  		offsetof(struct dm_name_list, name);
     301  	struct dm_name_list s;
     302  	uint32_t offset = ioc->data_start;
     303  	uint32_t offset_end = 0;
     304  	int rc;
     305  
     306  	if (ioc->data_start == ioc->data_size)
     307  		return;
     308  
     309  	if (abbrev(tcp)) {
     310  		tprint_array_next();
     311  		tprint_more_data_follows();
     312  		return;
     313  	}
     314  
     315  	for (uint32_t count = 0;; ++count) {
     316  		tprint_array_next();
     317  
     318  		if (count && offset <= offset_end)
     319  			goto misplaced;
     320  
     321  		offset_end = offset + name_list_name_offs;
     322  
     323  		if (offset_end <= offset || offset_end > ioc->data_size)
     324  			goto misplaced;
     325  
     326  		if (count >= max_strlen) {
     327  			tprint_more_data_follows();
     328  			break;
     329  		}
     330  
     331  		if (umove_or_printaddr(tcp, addr + offset, &s))
     332  			break;
     333  
     334  		tprint_struct_begin();
     335  		PRINT_FIELD_DEV(s, dev);
     336  		tprint_struct_next();
     337  		tprints_field_name("name");
     338  		rc = printstr_ex(tcp, addr + offset_end,
     339  				 ioc->data_size - offset_end,
     340  				 QUOTE_0_TERMINATED);
     341  
     342  		/*
     343  		 * In Linux v4.13-rc1~137^2~13 it has been decided to cram in
     344  		 * one more undocumented field after the device name, as if the
     345  		 * format decoding was not twisted enough already. So, we have
     346  		 * to check "next" now, and if it _looks like_ that there is
     347  		 * a space for one additional integer, let's print it. As if the
     348  		 * perversity with "name string going further than pointer to
     349  		 * the next one" wasn't enough. Moreover, the calculation was
     350  		 * broken for m32 on 64-bit kernels until v4.14-rc4~20^2~3, and
     351  		 * we have no ability to detect kernel bit-ness (on x86, at
     352  		 * least), so refrain from printing it for the DM versions below
     353  		 * 4.37 (the original version was also aligned differently than
     354  		 * now even on 64 bit).
     355  		 */
     356  
     357  		if ((rc > 0) && ioc->version[1] >= 37) {
     358  			kernel_ulong_t event_addr =
     359  				(addr + offset_end + rc + 7) & ~7;
     360  			uint32_t event_nr;
     361  
     362  			if ((event_addr + sizeof(event_nr)) <=
     363  			    (addr + offset + s.next) &&
     364  			    !umove(tcp, event_addr, &event_nr)) {
     365  				tprint_struct_next();
     366  				tprints_field_name("event_nr");
     367  				PRINT_VAL_U(event_nr);
     368  			}
     369  		}
     370  
     371  		tprint_struct_end();
     372  
     373  		if (!s.next)
     374  			break;
     375  
     376  		offset += s.next;
     377  	}
     378  
     379  	return;
     380  
     381  misplaced:
     382  	tprint_unavailable();
     383  	tprints_comment("misplaced struct dm_name_list");
     384  }
     385  
     386  static void
     387  dm_decode_dm_target_versions(struct tcb *const tcp, const kernel_ulong_t addr,
     388  			     const struct dm_ioctl *const ioc)
     389  {
     390  	static const uint32_t target_vers_name_offs =
     391  		offsetof(struct dm_target_versions, name);
     392  	struct dm_target_versions s;
     393  	uint32_t offset = ioc->data_start;
     394  	uint32_t offset_end = 0;
     395  
     396  	if (ioc->data_start == ioc->data_size)
     397  		return;
     398  
     399  	if (abbrev(tcp)) {
     400  		tprint_array_next();
     401  		tprint_more_data_follows();
     402  		return;
     403  	}
     404  
     405  	for (uint32_t count = 0;; ++count) {
     406  		tprint_array_next();
     407  
     408  		if (count && offset <= offset_end)
     409  			goto misplaced;
     410  
     411  		offset_end = offset + target_vers_name_offs;
     412  
     413  		if (offset_end <= offset || offset_end > ioc->data_size)
     414  			goto misplaced;
     415  
     416  		if (count >= max_strlen) {
     417  			tprint_more_data_follows();
     418  			break;
     419  		}
     420  
     421  		if (umove_or_printaddr(tcp, addr + offset, &s))
     422  			break;
     423  
     424  		tprint_struct_begin();
     425  		tprints_field_name("name");
     426  		printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
     427  			    QUOTE_0_TERMINATED);
     428  		tprint_struct_next();
     429  		PRINT_FIELD_U_ARRAY(s, version);
     430  		tprint_struct_end();
     431  
     432  		if (!s.next)
     433  			break;
     434  
     435  		offset += s.next;
     436  	}
     437  
     438  	return;
     439  
     440  misplaced:
     441  	tprint_unavailable();
     442  	tprints_comment("misplaced struct dm_target_versions");
     443  }
     444  
     445  static void
     446  dm_decode_dm_target_msg(struct tcb *const tcp, const kernel_ulong_t addr,
     447  			const struct dm_ioctl *const ioc)
     448  {
     449  	if (ioc->data_start == ioc->data_size)
     450  		return;
     451  
     452  	tprint_array_next();
     453  
     454  	if (abbrev(tcp)) {
     455  		tprint_more_data_follows();
     456  		return;
     457  	}
     458  
     459  	static const uint32_t target_msg_message_offs =
     460  		offsetof(struct dm_target_msg, message);
     461  	uint32_t offset = ioc->data_start;
     462  	uint32_t offset_end = offset + target_msg_message_offs;
     463  
     464  	if (offset_end > offset && offset_end <= ioc->data_size) {
     465  		struct dm_target_msg s;
     466  
     467  		if (umove_or_printaddr(tcp, addr + offset, &s))
     468  			return;
     469  
     470  		tprint_struct_begin();
     471  		PRINT_FIELD_U(s, sector);
     472  		tprint_struct_next();
     473  		tprints_field_name("message");
     474  		printstr_ex(tcp, addr + offset_end, ioc->data_size - offset_end,
     475  			    QUOTE_0_TERMINATED);
     476  		tprint_struct_end();
     477  	} else {
     478  		tprint_unavailable();
     479  		tprints_comment("misplaced struct dm_target_msg");
     480  	}
     481  }
     482  
     483  static void
     484  dm_decode_string(struct tcb *const tcp, const kernel_ulong_t addr,
     485  		 const struct dm_ioctl *const ioc)
     486  {
     487  	tprint_array_next();
     488  
     489  	if (abbrev(tcp)) {
     490  		tprint_more_data_follows();
     491  		return;
     492  	}
     493  
     494  	uint32_t offset = ioc->data_start;
     495  
     496  	if (offset <= ioc->data_size) {
     497  		tprint_struct_begin();
     498  		tprints_field_name("string");
     499  		printstr_ex(tcp, addr + offset, ioc->data_size - offset,
     500  			    QUOTE_0_TERMINATED);
     501  		tprint_struct_end();
     502  	} else {
     503  		tprint_unavailable();
     504  		tprints_comment("misplaced string");
     505  	}
     506  }
     507  
     508  static void
     509  dm_decode_payload(struct tcb *const tcp, const unsigned int code,
     510  		  const kernel_ulong_t arg, const struct dm_ioctl *const ioc)
     511  {
     512  	switch (code) {
     513  	case DM_DEV_WAIT:
     514  	case DM_TABLE_STATUS:
     515  		if (exiting(tcp) && !syserror(tcp))
     516  			dm_decode_dm_target_spec(tcp, arg, ioc);
     517  		break;
     518  	case DM_TABLE_LOAD:
     519  		if (entering(tcp))
     520  			dm_decode_dm_target_spec(tcp, arg, ioc);
     521  		break;
     522  	case DM_TABLE_DEPS:
     523  		if (exiting(tcp) && !syserror(tcp))
     524  			dm_decode_dm_target_deps(tcp, arg, ioc);
     525  		break;
     526  	case DM_LIST_DEVICES:
     527  		if (exiting(tcp) && !syserror(tcp))
     528  			dm_decode_dm_name_list(tcp, arg, ioc);
     529  		break;
     530  	case DM_LIST_VERSIONS:
     531  		if (exiting(tcp) && !syserror(tcp))
     532  			dm_decode_dm_target_versions(tcp, arg, ioc);
     533  		break;
     534  	case DM_TARGET_MSG:
     535  		if (entering(tcp))
     536  			dm_decode_dm_target_msg(tcp, arg, ioc);
     537  		else if (!syserror(tcp) && ioc->flags & DM_DATA_OUT_FLAG)
     538  			dm_decode_string(tcp, arg, ioc);
     539  		break;
     540  	case DM_DEV_RENAME:
     541  	case DM_DEV_SET_GEOMETRY:
     542  		if (entering(tcp))
     543  			dm_decode_string(tcp, arg, ioc);
     544  		break;
     545  	}
     546  }
     547  
     548  static int
     549  dm_known_ioctl(struct tcb *const tcp, const unsigned int code,
     550  	       const kernel_ulong_t arg)
     551  {
     552  	struct dm_ioctl *ioc = NULL;
     553  	struct dm_ioctl *entering_ioc = NULL;
     554  	bool ioc_changed = false;
     555  
     556  	if (entering(tcp)) {
     557  		ioc = malloc(sizeof(*ioc));
     558  		if (!ioc)
     559  			return 0;
     560  	} else {
     561  		ioc = alloca(sizeof(*ioc));
     562  	}
     563  
     564  	if ((umoven(tcp, arg, offsetof(struct dm_ioctl, data), ioc) < 0) ||
     565  	    (ioc->data_size < offsetof(struct dm_ioctl, data_size))) {
     566  		if (entering(tcp))
     567  			free(ioc);
     568  		return 0;
     569  	}
     570  	if (entering(tcp))
     571  		set_tcb_priv_data(tcp, ioc, free);
     572  	else {
     573  		entering_ioc = get_tcb_priv_data(tcp);
     574  
     575  		/*
     576  		 * retrieve_status, __dev_status called only in case of success,
     577  		 * so it looks like there's no need to check open_count,
     578  		 * event_nr, target_count, dev fields for change (they are
     579  		 * printed only in case of absence of errors).
     580  		 */
     581  		if (!entering_ioc ||
     582  		    (ioc->version[0] != entering_ioc->version[0]) ||
     583  		    (ioc->version[1] != entering_ioc->version[1]) ||
     584  		    (ioc->version[2] != entering_ioc->version[2]) ||
     585  		    (ioc->data_size != entering_ioc->data_size) ||
     586  		    (ioc->data_start != entering_ioc->data_start) ||
     587  		    (ioc->flags != entering_ioc->flags))
     588  			ioc_changed = true;
     589  	}
     590  
     591  	if (exiting(tcp) && syserror(tcp) && !ioc_changed)
     592  		return RVAL_IOCTL_DECODED;
     593  
     594  	if (entering(tcp))
     595  		tprint_arg_next();
     596  	else
     597  		tprint_value_changed();
     598  
     599  	tprint_array_begin();
     600  	if (dm_decode_header(tcp, code, arg, ioc))
     601  		dm_decode_payload(tcp, code, arg, ioc);
     602  	tprint_array_end();
     603  
     604  	return entering(tcp) ? 0 : RVAL_IOCTL_DECODED;
     605  }
     606  
     607  int
     608  dm_ioctl(struct tcb *const tcp, const unsigned int code, const kernel_ulong_t arg)
     609  {
     610  	switch (code) {
     611  	case DM_VERSION:
     612  	case DM_REMOVE_ALL:
     613  	case DM_LIST_DEVICES:
     614  	case DM_DEV_CREATE:
     615  	case DM_DEV_REMOVE:
     616  	case DM_DEV_RENAME:
     617  	case DM_DEV_SUSPEND:
     618  	case DM_DEV_STATUS:
     619  	case DM_DEV_WAIT:
     620  	case DM_TABLE_LOAD:
     621  	case DM_TABLE_CLEAR:
     622  	case DM_TABLE_DEPS:
     623  	case DM_TABLE_STATUS:
     624  	case DM_LIST_VERSIONS:
     625  	case DM_TARGET_MSG:
     626  	case DM_DEV_SET_GEOMETRY:
     627  	case DM_DEV_ARM_POLL:
     628  		return dm_known_ioctl(tcp, code, arg);
     629  	default:
     630  		return RVAL_DECODED;
     631  	}
     632  }