--- a/vmnet/Makefile +++ b/vmnet/Makefile @@ -43,7 +43,11 @@ INCLUDE += -I$(SRCROOT)/shared endif +ifdef KVERSION +VM_UNAME = $(KVERSION) +else VM_UNAME = $(shell uname -r) +endif # Header directory for the running kernel ifdef LINUXINCLUDE @@ -98,6 +102,13 @@ auto-build: $(DRIVER_KO) $(DRIVER): $(DRIVER_KO) if [ $< -nt $@ ] || [ ! -e $@ ] ; then cp -f $< $@; fi +# Use SUBDIRS on 2.x, 3.x, 4.x. Use M on newer kernels. +ifeq ($(filter-out 2 3 4,$(firstword $(subst ., ,$(VM_UNAME)))),) +DIRVAR := SUBDIRS +else +DIRVAR := M +endif + # # Define a setup target that gets built before the actual driver. # This target may not be used at all, but if it is then it will be defined @@ -107,7 +118,7 @@ prebuild:: ; postbuild:: ; $(DRIVER_KO): prebuild - $(MAKE) -C $(BUILD_DIR) SUBDIRS=$$PWD SRCROOT=$$PWD/$(SRCROOT) \ + $(MAKE) -C $(BUILD_DIR) $(DIRVAR)=$$PWD SRCROOT=$$PWD/$(SRCROOT) \ MODULEBUILDDIR=$(MODULEBUILDDIR) modules $(MAKE) -C $$PWD SRCROOT=$$PWD/$(SRCROOT) \ MODULEBUILDDIR=$(MODULEBUILDDIR) postbuild --- a/vmnet/bridge.c +++ b/vmnet/bridge.c @@ -684,14 +684,11 @@ } spin_unlock_irqrestore(&bridge->historyLock, flags); - /* - * We used to cli() before calling netif_rx() here. It was probably - * unneeded (as we never did it in netif.c, and the code worked). In - * any case, now that we are using netif_rx_ni(), we should certainly - * not do it, or netif_rx_ni() will deadlock on the cli() lock --hpreg - */ - - netif_rx_ni(clone); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0) + netif_rx_ni(clone); +#else + netif_rx(clone); +#endif # if LOGLEVEL >= 4 do_gettimeofday(&vnetTime); # endif --- a/vmnet/netif.c +++ b/vmnet/netif.c @@ -219,7 +219,11 @@ memset(&netIf->stats, 0, sizeof netIf->stats); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) memcpy(dev->dev_addr, netIf->port.paddr, sizeof netIf->port.paddr); +#else + eth_hw_addr_set(dev, netIf->port.paddr); +#endif if (register_netdev(dev) != 0) { LOG(0, (KERN_NOTICE "%s: could not register network device\n", @@ -311,7 +315,11 @@ /* send to the host interface */ skb->dev = netIf->dev; skb->protocol = eth_type_trans(skb, netIf->dev); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0) netif_rx_ni(skb); +#else + netif_rx(skb); +#endif netIf->stats.rx_packets++; return; @@ -498,7 +506,11 @@ return -EINVAL; } memcpy(netIf->port.paddr, addr->sa_data, dev->addr_len); - memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) + memcpy(dev->dev_addr, netIf->port.paddr, dev->addr_len); +#else + eth_hw_addr_set(dev, netIf->port.paddr); +#endif return 0; } --- a/vmnet/procfs.c +++ a/vmnet/procfs.c @@ -137,6 +137,7 @@ VNetProcShow(struct seq_file *p, // IN: } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0) /* *---------------------------------------------------------------------- * @@ -168,6 +169,7 @@ static struct file_operations fops = { .release = single_release, }; #endif +#endif /* @@ -203,7 +205,12 @@ VNetProcMakeEntryInt(VNetProcEntry *pa } else { ent->data = data; ent->fn = fn; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) + ent->pde = proc_create_single_data(name, mode, parent->pde, + VNetProcShow, ent); +#else ent->pde = proc_create_data(name, mode, parent->pde, &fops, ent); +#endif } if (ent->pde != NULL) { *ret = ent; --- a/vmnet/smac.c +++ b/vmnet/smac.c @@ -4118,7 +4118,7 @@ void SMACINT SMAC_SetMac(SMACState *state, // IN: state to update - uint8 *mac) // IN: pointer to host adapter's MAC + const uint8 *mac) // IN: pointer to host adapter's MAC { VNETKdPrintCall(("SMAC_SetMac")); ASSERT(state); --- a/vmnet/smac.h +++ b/vmnet/smac.h @@ -72,7 +72,7 @@ void SMACINT SMAC_InitState(struct SMACState **ptr); // IN: state to alloc/init void SMACINT -SMAC_SetMac(struct SMACState *state, uint8 *mac); // IN: state, and host MAC +SMAC_SetMac(struct SMACState *state, const uint8 *mac); // IN: state, and host MAC void SMACINT SMAC_CleanupState(struct SMACState **ptr); // IN: state to cleanup/dealloc --- a/vmnet/userif.c +++ b/vmnet/userif.c @@ -78,11 +78,11 @@ static int VNetUserIfSetUplinkState(VNe extern unsigned int vnet_max_qlen; #if COMPAT_LINUX_VERSION_CHECK_LT(3, 2, 0) -# define compat_kmap(page) kmap(page) -# define compat_kunmap(page) kunmap(page) -#else -# define compat_kmap(page) kmap((page).p) -# define compat_kunmap(page) kunmap((page).p) +# define skb_frag_page(frag) (frag)->page +# define skb_frag_size(frag) (frag)->size +#endif +#if COMPAT_LINUX_VERSION_CHECK_LT(5, 4, 0) +# define skb_frag_off(frag) (frag)->page_offset #endif /* @@ -137,16 +137,21 @@ UserifLockPage(VA addr) // IN */ static INLINE int -VNetUserIfMapPtr(VA uAddr, // IN: pointer to user memory +VNetUserIfMapPtr(VA64 uAddr, // IN: pointer to user memory size_t size, // IN: size of data struct page **p, // OUT: locked page void **ptr) // OUT: kernel mapped pointer { - if (!access_ok(VERIFY_WRITE, (void *)uAddr, size) || - (((uAddr + size - 1) & ~(PAGE_SIZE - 1)) != - (uAddr & ~(PAGE_SIZE - 1)))) { + uint8 v; + + /* Check area does not straddle two pages. */ + if ((uAddr & (PAGE_SIZE - 1)) + size > PAGE_SIZE) { return -EINVAL; } + /* Check if it is user's area. UserifLockPage() checks writability. */ + if (copy_from_user(&v, (void *)(unsigned long)uAddr, sizeof v) != 0) { + return -EFAULT; + } *p = UserifLockPage(uAddr); if (*p == NULL) { @@ -158,7 +163,7 @@ VNetUserIfMapPtr(VA uAddr, // IN: } static INLINE int -VNetUserIfMapUint32Ptr(VA uAddr, // IN: pointer to user memory +VNetUserIfMapUint32Ptr(VA64 uAddr, // IN: pointer to user memory struct page **p, // OUT: locked page uint32 **ptr) // OUT: kernel mapped pointer { @@ -201,7 +206,7 @@ VNetUserIfSetupNotify(VNetUserIF *userIf return -EBUSY; } - if ((retval = VNetUserIfMapUint32Ptr((VA)vn->pollPtr, &pollPage, + if ((retval = VNetUserIfMapUint32Ptr(vn->pollPtr, &pollPage, &pollPtr)) < 0) { return retval; } @@ -213,7 +218,7 @@ VNetUserIfSetupNotify(VNetUserIF *userIf goto error_free; } - if ((retval = VNetUserIfMapUint32Ptr((VA)vn->recvClusterPtr, + if ((retval = VNetUserIfMapUint32Ptr(vn->recvClusterPtr, &recvClusterPage, &recvClusterCount)) < 0) { goto error_free; @@ -511,6 +516,50 @@ /* *---------------------------------------------------------------------- * + * VNetCsumAndCopyToUser -- + * + * Checksum data and copy them to userspace. + * + * Results: + * folded checksum (non-zero value) on success, + * err set to 0 on success, negative errno on failure. + * + * Side effects: + * Data copied to the buffer. + * + *---------------------------------------------------------------------- + */ + +static unsigned int +VNetCsumAndCopyToUser(const void *src, // IN: Source + void *dst, // IN: Destination + int len, // IN: Bytes to copy + int *err) // OUT: Error code +{ + unsigned int csum; + +#if COMPAT_LINUX_VERSION_CHECK_LT(5, 10, 0) + csum = csum_and_copy_to_user(src, dst, len, 0, err); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0) + csum = csum_and_copy_to_user(src, dst, len); + *err = (csum == 0) ? -EFAULT : 0; +#else + if (!user_access_begin(dst, len)) { + *err = -EFAULT; + csum = 0; + } else { + *err = 0; + csum = csum_partial_copy_nocheck(src, dst, len); + user_access_end(); + } +#endif + return csum; +} + + +/* + *---------------------------------------------------------------------- + * * VNetCsumCopyDatagram -- * * Copy part of datagram to userspace doing checksum at same time. @@ -550,7 +555,7 @@ VNetCsumCopyDatagram(const struct sk_buf return -EINVAL; } - csum = csum_and_copy_to_user(skb->data + offset, curr, len, 0, &err); + csum = VNetCsumAndCopyToUser(skb->data + offset, curr, len, &err); if (err) { return err; } @@ -559,20 +569,20 @@ VNetCsumCopyDatagram(const struct sk_buf for (frag = skb_shinfo(skb)->frags; frag != skb_shinfo(skb)->frags + skb_shinfo(skb)->nr_frags; frag++) { - if (frag->size > 0) { + if (skb_frag_size(frag) > 0) { unsigned int tmpCsum; const void *vaddr; - vaddr = compat_kmap(frag->page); - tmpCsum = csum_and_copy_to_user(vaddr + frag->page_offset, - curr, frag->size, 0, &err); - compat_kunmap(frag->page); + vaddr = kmap(skb_frag_page(frag)); + tmpCsum = VNetCsumAndCopyToUser(vaddr + skb_frag_off(frag), + curr, skb_frag_size(frag), &err); + kunmap(skb_frag_page(frag)); if (err) { return err; } csum = csum_block_add(csum, tmpCsum, curr - buf); - curr += frag->size; + curr += skb_frag_size(frag); } } # Fixing VMWare Player on Linux when using DHCP addresses: https://www.nikhef.nl/~janjust/vmnet/ @@ -973,6 +989,9 @@ userIf = (VNetUserIF *)port->jack.private; hubJack = port->jack.peer; + /* never send link down events */ + if (!linkUp) return 0; + if (port->jack.state == FALSE || hubJack == NULL) { return -EINVAL; } --- a/vmnet/vnetEvent.c +++ b/vmnet/vnetEvent.c @@ -60,10 +60,12 @@ struct VNetEvent_EventNode { VNetEvent_EventNode *nextEvent; - VNet_EventHeader event; + union { + VNet_EventHeader header; + VNet_LinkStateEvent lse; + } event; }; -#define EVENT_NODE_HEADER_SIZE offsetof(struct VNetEvent_EventNode, event) struct VNetEvent_Mechanism { VNetKernel_SpinLock lock; /* mechanism lock */ @@ -369,6 +371,10 @@ return VNetKernel_EINVAL; } + if (e->size > sizeof(p->event)) { + return VNetKernel_EINVAL; + } + /* lock */ VNetKernel_SpinLockAcquire(&m->lock); m->handlerTask = VNetKernel_ThreadCurrent(); @@ -378,22 +384,15 @@ while (TRUE) { p = *q; if (p == NULL || - (p->event.eventId == e->eventId && p->event.type == e->type)) { + (p->event.header.eventId == e->eventId && p->event.header.type == e->type)) { break; } q = &p->nextEvent; } - /* remove previously sent event */ - if (p != NULL && p->event.size != e->size) { - *q = p->nextEvent; - VNetKernel_MemoryFree(p); - p = NULL; - } - /* insert new event into event list*/ if (p == NULL) { - p = VNetKernel_MemoryAllocate(EVENT_NODE_HEADER_SIZE + e->size); + p = VNetKernel_MemoryAllocate(sizeof(*p)); if (p == NULL) { m->handlerTask = NULL; VNetKernel_SpinLockRelease(&m->lock); @@ -485,8 +484,8 @@ while (s != NULL) { e = s->firstEvent; while (e != NULL) { - if ((e->event.classSet & classMask) != 0) { - h(data, &e->event); + if ((e->event.header.classSet & classMask) != 0) { + h(data, &e->event.header); } e = e->nextEvent; } From 650fb3abeb82f2b7d3a14f9579a7529d153636b7 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Fri, 7 Oct 2022 12:56:44 +0200 Subject: [PATCH] vmnet: work around field-spanning write warning (#195) While VMware 17.0.1 did work around the field-spanning write warning in VNetEvent_Send() by wrapping struct VNet_EventHeader into a union, they neglected to handle the same problem in VNetUserListenerEventHandler() so that running 17.0.1 on kernel >= 6.1-rc1 still issues memcpy: detected field-spanning write (size 28) of single field "&t->event" only this time it happens on VM start rather than on module load. Apply the same workaround as in 17.0.0 branch to avoid the warning. --- vmnet-only/vnet.h | 8 ++++++++ vmnet-only/vnetEvent.c | 2 +- vmnet-only/vnetUserListener.c | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/vmnet-only/vnet.h b/vmnet-only/vnet.h index d6691d5c..d5bb5572 100644 --- a/vmnet-only/vnet.h +++ b/vmnet-only/vnet.h @@ -274,6 +274,7 @@ typedef struct VNet_EventHeader { uint32 eventId; uint32 classSet; uint32 type; + char payload[]; } VNet_EventHeader; #pragma pack(pop) @@ -291,6 +292,13 @@ typedef struct VNet_LinkStateEvent { } VNet_LinkStateEvent; #pragma pack(pop) +static inline void VNet_Event_copy(VNet_EventHeader *dst, + const VNet_EventHeader *src) +{ + *dst = *src; + memcpy(dst->payload, src->payload, src->size - sizeof(*src)); +} + /* *---------------------------------------------------------------------------- */ diff --git a/vmnet-only/vnetEvent.c b/vmnet-only/vnetEvent.c index f1579292..12036070 100644 --- a/vmnet-only/vnetEvent.c +++ b/vmnet-only/vnetEvent.c @@ -401,7 +401,7 @@ VNetEvent_Send(VNetEvent_Sender *s, // IN: a sender p->nextEvent = s->firstEvent; s->firstEvent = p; } - memcpy(&p->event, e, e->size); + VNet_Event_copy(&p->event.header, e); /* send event */ classSet = e->classSet; diff --git a/vmnet-only/vnetUserListener.c b/vmnet-only/vnetUserListener.c index 114f3907..e9f51755 100644 --- a/vmnet-only/vnetUserListener.c +++ b/vmnet-only/vnetUserListener.c @@ -226,7 +226,7 @@ VNetUserListenerEventHandler(void *context, // IN: the user listener return; } t->nextEvent = NULL; - memcpy(&t->event, e, e->size); + VNet_Event_copy(&t->event, e); /* append event to event list */ userListener = (VNetUserListener*)context; From 0ca979d4bd06144204d720bb82f0a1e29024f9fa Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Wed, 18 Jan 2023 00:19:52 +0100 Subject: [PATCH] vmnet: use explicit module_init() and module_exit() (#187) While vmmon module already uses explicit module_init() and module_exit() for its init and cleanup function, vmnet relies on traditional magic names init_module() and cleanup_module(). Apparently this has an unfortunate side effect that the two functions are not identified as indirect call targets by objdump and they get "sealed" when the module is built against and loaded into an IBT enabled kernel. Starting with 6.3-rc1, objtool is going to warn about this issue, indicating that the legacy module initialization is deprecated and module_init() and module_exit() macros should be used instead so do that for vmnet as well. --- vmnet-only/driver.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/vmnet-only/driver.c b/vmnet-only/driver.c index 197a213..b22cbfb 100644 --- a/vmnet-only/driver.c +++ b/vmnet-only/driver.c @@ -279,7 +279,7 @@ VNetRemovePortFromList(const VNetPort *port) // IN: port to remove from list /* *---------------------------------------------------------------------- * - * init_module -- + * vmnet_init_module -- * * linux module entry point. Called by /sbin/insmod command. * Initializes module and Registers this driver for a @@ -296,7 +296,7 @@ VNetRemovePortFromList(const VNetPort *port) // IN: port to remove from list */ int -init_module(void) +vmnet_init_module(void) { int retval; @@ -358,7 +358,7 @@ init_module(void) /* *---------------------------------------------------------------------- * - * cleanup_module -- + * vmnet_cleanup_module -- * * Called by /sbin/rmmod. Unregisters this driver for a * vnet major #, and deinitializes the modules. The 64-bit @@ -375,7 +375,7 @@ init_module(void) */ void -cleanup_module(void) +vmnet_cleanup_module(void) { unregister_chrdev(VNET_MAJOR_NUMBER, "vmnet"); VNetProtoUnregister(); @@ -1701,3 +1701,5 @@ MODULE_LICENSE("GPL v2"); * by default (i.e., neither mkinitrd nor modprobe will accept it). */ MODULE_INFO(supported, "external"); +module_init(vmnet_init_module); +module_exit(vmnet_cleanup_module);