undici is an HTTP client for Node.js. In versions 7.0.0 through 7.30.1 and 8.0.0 through 8.12.3, when an Agent composes the redirect and shared cache interceptors, a cached response that is revalidated with a conditional request can be validated by a 304 Not Modified from a different origin. When the origin that stored the response redirects the conditional revalidation request to another origin, the redirect handler strips origin-bound credential headers but not the cache-generated If-None-Match or If-Modified-Since validators, so the redirect destination receives the original origin's validator and undici treats its 304 as successful revalidation of the stored representation. As a result, a shared cache can serve the original origin's stored authenticated response, including one gated by Authorization, to a later unauthenticated caller. The issue is fixed in undici 7.30.2 and 8.12.4, and users should upgrade to one of those versions or later. As a workaround, compose the interceptors as cache then redirect, use per-origin dispatchers and cache stores, or disable shared caching on redirect-capable requests.
undici is an HTTP client for Node.js. In versions 7.0.0 through 7.30.1 and 8.0.0 through 8.12.3, when the redirect interceptor and a shared response cache are used together, an authenticated request that is redirected to a same-origin target has the final response stored under the original request's cache key. That key omits the credential and carries no Vary header, so a later anonymous or different user can be served the first user's private response body. This results in cross-user disclosure of cached private responses. The issue is fixed in undici 7.30.2 and 8.12.4, and users should upgrade to one of those versions or later.
undici is an HTTP client for Node.js. In versions 7.0.0 through 7.30.1 and 8.0.0 through 8.12.3, when the cache interceptor is composed with a downstream handler that throws during response processing (such as the built-in dump interceptor exceeding its maxSize, or the decompress interceptor rejecting more than five Content-Encoding values), a throw raised asynchronously while a cached response is replayed escapes as an uncaught exception or unhandled promise rejection and terminates the Node.js process. A malicious or compromised HTTP server can crash any such client by serving a cacheable response that triggers the throw on replay, including on a 304 revalidation of a stale entry, so the impact is availability only. The issue is fixed in undici 7.30.2 and 8.12.4, and users should upgrade to one of those versions or later. As a workaround, avoid composing the cache interceptor with a response handler that throws during response processing until upgrading.
undici is an HTTP client for Node.js. In versions 7.0.0 through 7.30.1 and 8.0.0 through 8.12.3, the redirect interceptor strips sensitive headers such as Authorization, Cookie, Proxy-Authorization, and Host when a request is redirected to a different origin, but when request headers are supplied as an array of name and value tuples (a valid documented input, for example from Object.entries), the redirect handler misreads them as a flat list so the header names never match and the sensitive headers are not removed. If a header-normalizing interceptor such as cache or deduplicate is composed outside the redirect interceptor, it then serializes the retained headers onto the cross-origin request, disclosing the caller's origin-bound credentials to the unrelated redirect destination. This is a representation-dependent bypass of the cross-origin header stripping added in CVE-2024-30260. The issue is fixed in undici 7.30.2 and 8.12.4, and users should upgrade to one of those versions or later. As a workaround, supply request headers as a plain object or a flat name and value array rather than an array of tuples, or compose the redirect interceptor outside the normalizing interceptor.
undici is an HTTP client for Node.js. In versions 7.15.0 through 7.30.1 and 8.0.0 through 8.12.3, fetch verifies Subresource Integrity metadata by splitting the integrity string on a single space, but the SRI grammar separates metadata by ASCII whitespace, which also includes the horizontal tab. An integrity value whose digest is preceded by a tab or other ASCII whitespace is parsed as a single unrecognized token and skipped, and when no recognized algorithm remains undici treats the integrity list as empty and accepts the response, so a server returning a body different from the pinned digest passes the check. An application that pins a response with a valid but whitespace-surrounded SRI value receives attacker-controlled bytes with no integrity error, and the attacker needs only to control the response body, not the integrity metadata. The issue is fixed in undici 7.30.2 and 8.12.4, and users should upgrade to one of those versions or later. As a workaround, supply SRI integrity metadata without surrounding whitespace, using a single space to separate multiple digests.
undici is an HTTP client for Node.js. In versions 7.30.1 and 8.12.2 through 8.12.3, when an originless Agent composes the redirect and shared cache interceptors, undici decides whether to store the final response under the original request key by inspecting only the final redirect hop, which bypasses the earlier fix for CVE-2026-109829 that compared only the initial and final origins. A redirect that bounces through an untrusted origin and returns to the trusted origin is still cached under the trusted key, an intermediate hop's Vary is dropped so a response that varies on Authorization or Cookie can be served to a different user, and an intermediate hop's no-cache or default-uncacheable status is ignored so a non-storable redirect is laundered into a cacheable entry. An attacker who controls a redirect target from a trusted URL can poison the cached entry for that URL or have a private, per-user response served to a later caller. The issue is fixed in undici 7.30.2 and 8.12.4, and users should upgrade to one of those versions or later. As a workaround, compose the interceptors as cache then redirect, use per-origin dispatchers and cache stores, or disable shared caching on redirect-capable requests.
dm cache: fix issue with background work locking
Drivers: hv: Avoid infinite retry loop in initvpindex()
In the Linux kernel, the following vulnerability has been resolved:
pidnamespace: make zappidnsprocesses() freezable
When the init of a pid namespace exits, zappidnsprocesses() waits until all other pids in the namespace are freed. A tracer outside the namespace that does not reap its traced zombies keeps those pids allocated, so this wait has no upper bound, and while it lasts every suspend attempt fails:
Freezing user space processes failed after 20.006 seconds (2 tasks refusing to freeze, wqbusy=0):
Neither wait is freezable. The first loop sleeps in dowait(), which returns -ERESTARTSYS on the freezer's fake signal; the loop then clears TIFSIGPENDING and waits again without ever freezing. The final loop sleeps in TASKINTERRUPTIBLE, and since nothing clears TIFSIGPENDING there, schedule() returns immediately after the first freeze attempt and the task spins until the wait ends.
Sleep in TASKINTERRUPTIBLE | TASKFREEZABLE in dowait(), so that the freezer can freeze tasks waiting there directly, and in TASKIDLE | TASKFREEZABLE in the final loop, like coredumptaskexit() does.
This was hit with Chromium-based browsers: crashpad ptrace-attaches the threads of a crashed renderer, which is a pid namespace init, and does not reap those killed during the dump.
In the Linux kernel, the following vulnerability has been resolved:
tracing: Do not initialize tracefs work if tracing is disabled
If trace buffer allocation fails during early boot (e.g. in earlytraceinit() -> allocatetracebuffers()), tracingdisabled is left set to 1 and the global trace array events list remains uninitialized.
Later during boot, tracerinittracefs() (invoked via fsinitcall) queues tracerfsinitwork asynchronously without checking if tracing was disabled. When tracerinittracefsworkfunc() runs, it calls eventtraceinit() and inittracertracefs(&globaltrace, NULL) unconditionally. This leads to findeventfile() dereferencing an uninitialized tr->events list, causing an early boot NULL pointer dereference Oops:
BUG: Kernel NULL pointer dereference on read at 0x00000010 Faulting instruction address: 0xc000000000485be0 Oops: Kernel access of bad area, sig: 7 [#1] ... NIP [c000000000485be0] findeventfile+0x70/0x3c0 LR [c000000000448874] inittracertracefs+0x274/0xc80 Call Trace: inittracertracefs+0x274/0xc80 tracerinittracefsworkfunc+0x50/0x320 processonework+0x1e8/0x5c0 workerthread+0x1dc/0x3d0 kthread+0x194/0x1b0 startkernelthread+0x14/0x18
Check tracingdisabled at the beginning of tracerinittracefsworkfunc() and return immediately if tracing is disabled or failed to initialize, preventing the crash.
In the Linux kernel, the following vulnerability has been resolved:
iouring/net: don't retry recv on uncommitted buffers
Commit 41b70df5b38b ("iouring/net: commit partial buffers on retry") commits ring provided buffers when a receive is going to be retried after a partial transfer, so that another request using the same buffer group can't be handed the memory the first one keeps receiving into. It commits the number of bytes received so far. That does the job for a single buffer which is consumed as a whole, but the request hangs on to all it has mapped:
- For a buffer ring with IOUPBUFRINGINC only these bytes are consumed. The rest of the buffer stays at the head of the ring. - For a bundle only the buffers which hold these bytes are consumed, the others that were peeked stay in the ring.
In both cases the next receive with that buffer group gets memory that the retried request is still going to write to, and whoever comes last overwrites the data of the other. What the retry fills isn't committed at all, as the buffer list isn't known any more at that point. So the kernel hands it out again later on, while the CQE tells the application that it was used, and for an incremental buffer that it is done with.
The remainder can't be committed in advance: if the request ends up receiving less, there is no way to tell the application that more was consumed than the CQE accounts for. Complete the request with what it has got instead of retrying it. MSGWAITALL is no longer honoured for receives into incremental buffers or multi buffer bundles, they can return a short count, with the request marked as failed like for other short MSGWAITALL transfers.
In the Linux kernel, the following vulnerability has been resolved:
iouring: wake ring pollers for local taskwork
With IORINGSETUPDEFERTASKRUN completions are queued as local work, and only become CQEs when the submitter enters the kernel to wait for events. For an application that finds out via poll() or epoll on the ring fd when it is time to do that, iouringpoll() reports EPOLLIN if local work is pending.
That only helps if the work is there at the time of the call. When DEFERTASKRUN was added, ioreqlocalworkadd() did iocqringwake(), which took care of tasks sleeping in poll() as well. Then polling got a wait queue of its own, ->pollwq, and the wake up in ioreqlocalworkadd() was replaced by waking the submitter directly. Since then ->pollwq is only woken when CQEs are committed, which for such a ring is done by the very task that is waiting to be told to:
submitter completion poll(ringfd) iouringpoll() // nothing yet schedule() ioreqlocalworkadd() // ->cqwaitnr: nobody waits // sleeps on
Wake ->pollwq when the first entry is put on the work list. Like for the eventfd, that must not be done right away if the work is queued from a wake up handler, as the chain of wake ups could get back to the wait queue whose lock is held. Use an RCU callback in that case, with a reference to the ring.
In the Linux kernel, the following vulnerability has been resolved:
iouring/napi: make stale entries time out after 60s
With dynamic tracking a NAPI id that hasn't been seen for a minute is meant to be dropped from the list the ring busy polls. The timeout is added to jiffies, but it is defined as 60 SECCONVERSION, and SECCONVERSION isn't the number of jiffies in a second. It is the internal factor of timespec64tojiffies(), HZ scaled up by SECJIFFIESC bits. For HZ=1000 that is 1000 << 22, which puts the timeout at about eight years. On 32-bit the multiplication overflows.
So entries are never removed, and every id that a socket of the ring ever had keeps being polled on each wait.
In the Linux kernel, the following vulnerability has been resolved:
iouring/poll: avoid retry wakeup loss when dropping ownership
If a wakeup sets IOPOLLRETRYFLAG between the owning task consuming the existing instance of IOPOLLRETRYFLAG and the loop check of iopollcheckevents(), then we stall on processing the request until an unrelated wakeup comes along.
Using atomiccmpxchg instead of atomicsubreturn to guarantee both the count v and pollrefs are 0 and no wake-ups are missed.
In the Linux kernel, the following vulnerability has been resolved:
iouring/timeout: don't lose a cancel as io-wq arms a timeout
An IORINGOPTIMEOUT run by io-wq, because of IOSQEASYNC, has the same problem as poll, futex and waitid. iowqsubmitwork() looks at IOWQWORKCANCEL once, before it issues the request. A cancel in between flags the work, gets -EALREADY, and searches ->timeoutlist in vain, while the worker then adds the request and starts the timer:
io-wq worker iotrycancel() iowqsubmitwork() IOWQWORKCANCEL isn't set ioasynccancelone() sets IOWQWORKCANCEL = -EALREADY iotimeoutcancel() = -ENOENT iotimeout() adds the request to ->timeoutlist hrtimerstart() = IOUISSUESKIPCOMPLETE
Nobody looks at the flag again, and the timeout runs its course. At ring exit, iouringtrycancelrequests() cancels io-wq work and then kills the timeouts the same way, so an exiting task can get stuck for the full duration of such a timeout.
Look at the flag under ->timeoutlock, which the search takes as well, and fail the request if it is set. If the search came first, the flag is visible. If it comes later, it finds the request on the list.
In the Linux kernel, the following vulnerability has been resolved:
iouring/zcrx: fix buffer offsets in areas above 4GB
iozcrxqueuecqe() computes the offset of the buffer in its area from netiovidx(), which is an unsigned int, so the shift is done in 32 bits and only the result is widened to the u64 that goes into the CQE. An area can be up to 1TB in size. For every niov beyond the first 4GB the CQE points the application at a buffer 4GB, or a multiple of that, below the one which has the data. It reads the wrong data, and then returns a buffer it doesn't own via the refill queue while the real one is never given back.
In the Linux kernel, the following vulnerability has been resolved:
iouring/openclose: keep the lock in iopipefixed()
iopipefixed() installs both ends of the pipe in the fixed file table, drops ->uringlock, and copies the slot numbers to the application. If that faults, it takes the lock again and removes whatever is in these slots.
If the request is run by io-wq the lock really is dropped in between, and the slots are visible to everybody else. By the time the error path gets to them, they may have been closed and reused, by IORINGOPCLOSE and a direct open or accept, or by IORINGREGISTERFILESUPDATE. Then a file is removed from the table that has nothing to do with the failed request.
Do the copy with the lock held. Faulting in user memory under ->uringlock is fine, the registration paths do it all the time.
In the Linux kernel, the following vulnerability has been resolved:
KEYS: Fix keyrejectandlink() race with keyring restriction
The restrictlink check in keyrejectandlink() is done before taking the keyring semaphore, racing with a concurrent keyringrestrict() that installs the restriction while holding the semaphore in write mode.
Move the check to after keylinklock(), so it is done under the semaphore, matching the pattern used in keycreateorupdate().
In the Linux kernel, the following vulnerability has been resolved:
io-wq: order the exit bit against worker creation task work
iowqexitworkers() cancels task work for queued worker creation with taskworkcancelmatch(). That has a plain load of task->taskworks.
ioqueueworkercreate() adds the new entry and tests IOWQBITEXIT to cancel it if the workqueue is already on its way out.
That must be ordered. taskworkadd() has a full barrier via cmpxchg(). But iowqexitstart() sets the bit with setbit() which doesn't have any memory ordering.
So afaict, on weakly ordered architectures the exiting task may load taskworks before the store of the bit is visible. The other side tests the bit before the store is visible as well. So the entry remains queued with workerrefs and the exiting task waits on workerdone indefinitely.
If the task still has rings then iouringdeltctxnode() provides the barrier via testandsetbit() in iowqsetexitonidle(). When it has closed all rings though that barrier is gone.
Add the barrier after the setbit().
In the Linux kernel, the following vulnerability has been resolved:
iouring: don't flood a cancelling task with taskwork
ioringexitwork() asks each task which has used the ring to remove its tctx node, by taskwork, and repeats that until the list is empty. iotctxexitcb() doesn't touch the node while the task is in iouringcancelgeneric(), which removes all the nodes at its end. So the work item queues the callback again as soon as it has completed, for nothing.
That is more than a waste of time. taskworkrun() only returns when it finds the list empty. If the rings are released from inside the cancellation, because requests of other rings held the last references to their files, up to 64 work items keep sending callbacks to the one task which is running them. It never finds the list empty, never gets back to the cancellation, the nodes stay, and so it goes on: a task with 60 pairs of rings which poll each other ran 280000 callbacks in 5 seconds, 20 seconds after exitgroup(). It ends when all the senders happen to be late at the same time. Until then the task keeps a CPU and the work items busy.
An exiting task can't be killed meanwhile. execve() cancels the requests in the same way, so a live task can get stuck there as well, for minutes. If it takes more than five, the work items run into the WARNONONCE(timeafter(jiffies, timeout)) of ioringexitwork(). No privilege is needed for any of this.
If the callback has met a task in cancellation, wait a little before it is sent again. Nobody has to wake the work item up for that: either the node is gone by then, or it is the turn of the next round.
In the Linux kernel, the following vulnerability has been resolved:
ntfs: fix resident conversion in ntfsnewattrflags
When setting sparse/compressed flags on a resident attribute, the function skipped the resident-to-non-resident conversion and terminated.
In the Linux kernel, the following vulnerability has been resolved:
iouring/io-wq: don't hide a link from cancellations
A task that exits cancels its tracked requests and waits until they are gone. iouringcancelgeneric() looks everywhere a request can be, and goes to sleep if it found nothing to cancel. It is woken up each time one of the requests of the task is completed by task work or freed.
When io-wq is done with a request that has a link, there is a time in which the next request of the link can't be found:
ioworkerhandlework() ioassigncurrentwork(worker, NULL) iowqfreework(work) ioreqfindnext() // takes the link off the request iofreereq() // task work for the task ioassigncurrentwork(worker, linked) ioruntaskwork() condresched() worker->curwork = linked
It is not pending, it is not the current work of a worker, and it is not yet on the list that it is about to be parked on. If the pass that the exiting task makes because of iofreereq() falls into that time, it finds nothing and the task goes to sleep. The worker then runs the link, which IOWQBITEXIT doesn't prevent for bounded work. If that parks the request nothing is completed or freed, and nobody wakes up the task. If it is the process that exits it can't be killed any more either.
NOP, IOSQEASYNC | IOSQEIOLINK FUTEXWAIT, FUTEX2PRIVATE exit of the thread, or of the process, up to 63us later
With four of these per round 5 to 8 of 10,000 rounds hang (4 CPUs, KVM):
iouringcancelgeneric doexit
Commit f8b632e89a10 ("iouring: tighten task exit cancellations") is about the same gap, and makes the task look again for whatever happens before iofreereq().
Free the request once the link is the current work of the worker, or pending. Whoever is woken up by that can find the link, until it is parked where cancellations look as well. To do so split iowqfreework() in two.
In the Linux kernel, the following vulnerability has been resolved:
tipc: protect node reset trace dump with node lock
The tipcnoderesetlinks trace event asks tipcnodedump() to walk the node's link entries. Unlike the other node events that request link data, this event runs without the node lock.
This permits bearer teardown to free a link while the trace callback is dumping it:
CPU 0 CPU 1 tracetipcnoderesetlinks() tipcnodedump() l = n->links[0].link tipcnodewritelock() kfree(l) n->links[0].link = NULL tipcnodewriteunlock() tipclinkdump(l)
tipclinkdump() then dereferences the stale pointer. KASAN reported:
BUG: KASAN: slab-use-after-free in tipclinkdump Read of size 4 by task poc/115 Call Trace: tipclinkdump+0x10cb/0x16b0 tipcnodedump+0x4bb/0x740 traceeventraweventtipcnodeclass+0x258/0x360 tipcnoderesetlinks+0x14d/0x1a0 tipcrcv+0x13f5/0x3030 tipcudprecv+0x4e3/0x670 Allocated by task 0: tipclinkcreate+0x1e1/0x1020 tipcnodecheckdest+0x7d2/0x11a0 tipcdiscrcv+0xdbf/0x1430 Freed by task 89: kfree+0x131/0x3c0 tipcnodelinkdown+0x267/0x4b0 tipcnodedeletelinks+0xec/0x160 bearerdisable+0x107/0x260
Take the node write lock around the trace event. This serializes the dump against tipcnodelinkdown(delete=true), which frees the link under the same write lock.
drm/amdgpu: correct reservation fence slots for userq per-vm BOs eviction
A flaw was found in libslirp in icmpforwarderror() in src/ipicmp.c. When constructing an ICMP error packet, the length of the quoted original packet (siplen) was derived from the IP header field iplen without clamping it to the actual mbuf length (msrc->mlen). A truncated or malformed IPv4 packet could cause memcpy to read past the end of the source mbuf on the host heap, embedding residual host memory into the ICMP error packet returned over the virtual network interface. An unprivileged guest or container process able to send crafted packets through libslirp could disclose host heap contents.
A flaw was found in libslirp in ifoutput() in src/if.c. When demoting an interactive session from the fast queue to the batch queue after throughput limits were exceeded, the code called slirpremque() and slirpinsque() on ifm->mnextpkt (an arbitrary packet in the circular session list) instead of the session queue head. This corrupted doubly-linked queue pointers. Subsequent traversals in ifstart() could crash the host process that embeds libslirp (for example QEMU or slirp4netns), resulting in a guest-to-host denial of service.
In the Linux kernel, the following vulnerability has been resolved:
tracing: Reject incomplete event filter expressions
A non-empty expression normally finishes in predicateparse()'s inner loop, which resolves the pending logical operators before jumping to finalization. If the input ends after an operator, possibly followed by unary '!' tokens, the outer loop instead falls through with unresolved operator state.
For example, "commonpid == 1 || commonpid == 2 &&" leaves prog[0].target equal to -1. The second pass then reads prog[-1] before the forward-jump check can reject it. Depending on adjacent slab contents, it can also copy an out-of-bounds word into a branch target that passes the forward-only check. A self-targeted perf tracepoint counting event can reach this path through PERFEVENTIOCSETFILTER.
Reject the outer-loop fallthrough when a predicate has already been parsed. This ensures that non-empty expressions only reach finalization after the inner loop has resolved their operators. Keep the existing no-filter error for input that has no predicates. Add a regression test for trailing logical operators and unary negations.
In the Linux kernel, the following vulnerability has been resolved:
tracing: Save the event file instead of the compatible histogram
createfieldvarhist() creates a histogram on the matched event to supply a field variable, and records it on the target so it can be torn down later:
histdata = findcompatiblehist(targethistdata, file); ... / Save the compatible histogram information / varhist->histdata = histdata;
histdata is not the histogram it creates. It is one that was already there, whose keys the new one copies. The saved pointer has a single user, unregisterfieldvarhists(), which runs when the target is removed:
file = histdata->fieldvarhists[i]->histdata->eventfile;
findcompatiblehist() matches on keys alone, so it can pick one with no variables of its own. checkvarrefs() then returns false and the user can remove it while the target still points at it:
BUG: KASAN: slab-use-after-free in eventhisttriggerfree+0x2b2/0x320 Read of size 8 at addr ffff8880093b90e0 by task init/1
Freed by task 1: kfree+0x154/0x420 eventhisttriggerfree+0x1d5/0x320 eventhisttriggerparse+0x35f3/0x69e0 triggerprocessregex+0x1a6/0x250
Save the event file instead. It is all the dereference ever wanted, and it does not go away when the user removes a trigger.
In the Linux kernel, the following vulnerability has been resolved:
tracing: Unregister field variable histograms when the command fails
An action that names a field on another event needs that field as a variable, so createfieldvarhist() registers a second hist trigger on that event to provide it, and records it in targethistdata->fieldvarhists[].
If a later part of the same command fails, createactions() returns an error and eventhisttriggerparse() jumps to outfree. The write fails with -EINVAL and schedswitch gets no trigger, but:
# echo 'hist:keys=pid:ts0=commontimestamp.usecs' >> \ events/sched/schedwaking/trigger # echo 'mysynth u64 lat; int a; int b' >> syntheticevents # echo 'hist:keys=nextpid:wakeuplat=commontimestamp.usecs-$ts0:\ onmatch(sched.schedwaking).mysynth($wakeuplat,prio,nosuchfld)'\ >> events/sched/schedswitch/trigger # cat events/sched/schedwaking/trigger hist:keys=pid:vals=hitcount:ts0=commontimestamp.usecs:sort=hitcount:size=2048:clock=global [active] hist:keys=pid:vals=hitcount:syntheticprio=prio:sort=hitcount:size=2048 [active]
The second histogram is the one created for 'prio'. It is still active and still counting.
outfree reaches destroyfieldvarhists(), which frees the fieldvarhists[] entries, but never unregisterfieldvarhists(), which is what removes the triggers and runs only from eventhisttriggerfree(). The entry is freed and the trigger is left with nothing tracking it.
Unregister the field variable histograms in outfree as well, and record each one as soon as its trigger is registered rather than after the lookup that follows it, so no exit can leave a registered trigger untracked. The entry then belongs to fieldvarhists[] and is released by destroyfieldvarhists(), so freeing it on that exit would be a double free.
In the Linux kernel, the following vulnerability has been resolved:
tracing/histograms: Verify eventcall identity when updating synthevent refcount
In eventhisttriggerparse() and histunregall(), the histogram registration and unregistration paths look up findsynthevent() using traceeventname(file->eventcall) and adjust se->ref if a match is found.
Because findsynthevent() only compares the event basename, registering or removing a histogram on a non-synthetic trace event whose basename matches a synthetic event erroneously modifies the synthetic event's reference count. If a histogram is registered on the non-synthetic event before the synthetic event is created and removed afterwards, se->ref is decremented without a matching increment, allowing the synthetic event to be freed while an active histogram action still holds a pointer to it.
Introduce findsyntheventcall() to verify that &se->call matches file->eventcall before adjusting se->ref.