From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.9 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, FSL_HELO_FAKE,HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI, MENTIONS_GIT_HOSTING,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9599FC636CD for ; Sun, 18 Jul 2021 00:42:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7F52661156 for ; Sun, 18 Jul 2021 00:42:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232926AbhGRApi (ORCPT ); Sat, 17 Jul 2021 20:45:38 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:41744 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230259AbhGRApg (ORCPT ); Sat, 17 Jul 2021 20:45:36 -0400 Received: from mail-pg1-x52f.google.com (mail-pg1-x52f.google.com [IPv6:2607:f8b0:4864:20::52f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 68942C061762; Sat, 17 Jul 2021 17:42:38 -0700 (PDT) Received: by mail-pg1-x52f.google.com with SMTP id t9so14800815pgn.4; Sat, 17 Jul 2021 17:42:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:content-transfer-encoding:in-reply-to; bh=nE+L/Xja+gxlPGZE3r8KM767jVoS3CfJjp8H3gvsYtU=; b=DR5rLyV1O5knGWcJzvggVVavgcLr/uK1QFJ2D3GY38KomcwkRunt2BTi92PoHQZJG3 7XDjZgNrVd3kyuDEA2XjRwAMTSF06POe2nrSb/IS8W5wyQenkzb+BnKazoelzJl7bLkR KZyFASV55i2xf4BCyTrfXvJFfrvmjxjF6z1UwKJjU4VT9IftdSjPRXhOx+iM3jJc3n8S R3m3EsbQy+ceSGqDIa2ZqFy5SlqxmN5ozs7yaO4nDJPqCwoCRyEG54wxQ9z/addqy/RE gOG4KvAJonXjJbG0D5ySiCfJkdCjn/igCaV0TPL5ouDcDE66EyV3RLDOeIv32ZwGa+wJ qFMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:content-transfer-encoding :in-reply-to; bh=nE+L/Xja+gxlPGZE3r8KM767jVoS3CfJjp8H3gvsYtU=; b=hfXjaTq3LvSUfiubUzUCyl/i8oYqxapX3CJnSpd51I/RxiKzagSMCjQhUHK7tUliIw No4nloXe9l4OkVfEr/hx2yMmyeaH9Xz6JlzrNO4v178yhOjWx3pfNEqmKlzvIAQ8GbTV 1hTvLiX7UhsjFDKaHHjZwZ9cZyTsnwY0LCvGZwaaYpks3pPUDm552Zr+BooAZ6Oa97Pa BHK2EY+HTYPX/5sLnfCQLtbn0swPHKJHo4sAUZpYcxIV3k4FnANsHsE16Wu5JWdO+EaH MEF8j0bkO22UC0cczsHNEQBRgcOp4P6tDmIp92ggJkqnqAJeTAB5YIdx9aaosOZATL8M TBFQ== X-Gm-Message-State: AOAM533Qy6eWN/HuDqcpGGaOfbIDjk0IbrPlgHlif+MlI4XpJI/9rprE Px4vhBSsdI6jBOw777BCif0= X-Google-Smtp-Source: ABdhPJwnHMVN3lApnkrs+/nrBY9CHlMQPq4uS3Zr9vLvMcgS5woeLjkNrSJNA/WeXhNk213TwtBUzg== X-Received: by 2002:a63:3584:: with SMTP id c126mr17559798pga.33.1626568957780; Sat, 17 Jul 2021 17:42:37 -0700 (PDT) Received: from gmail.com ([2601:600:8500:5f14:d627:c51e:516e:a105]) by smtp.gmail.com with ESMTPSA id a24sm7777228pgj.12.2021.07.17.17.42.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 17 Jul 2021 17:42:37 -0700 (PDT) Date: Sat, 17 Jul 2021 17:38:52 -0700 From: Andrei Vagin To: Jann Horn Cc: kernel list , Linux API , linux-um@lists.infradead.org, criu@openvz.org, avagin@google.com, Andrew Morton , Andy Lutomirski , Anton Ivanov , Christian Brauner , Dmitry Safonov <0x7f454c46@gmail.com>, Ingo Molnar , Jeff Dike , Mike Rapoport , Michael Kerrisk , Oleg Nesterov , Peter Zijlstra , Richard Weinberger , Thomas Gleixner , linux-mm@kvack.org Subject: Re: [PATCH 0/4 POC] Allow executing code and syscalls in another address space Message-ID: References: <20210414055217.543246-1-avagin@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Fri, Jul 02, 2021 at 05:12:02PM +0200, Jann Horn wrote: > On Fri, Jul 2, 2021 at 9:01 AM Andrei Vagin wrote: > > On Wed, Apr 14, 2021 at 08:46:40AM +0200, Jann Horn wrote: > > > On Wed, Apr 14, 2021 at 7:59 AM Andrei Vagin wrote: > > > > We already have process_vm_readv and process_vm_writev to read and write > > > > to a process memory faster than we can do this with ptrace. And now it > > > > is time for process_vm_exec that allows executing code in an address > > > > space of another process. We can do this with ptrace but it is much > > > > slower. > > > > > > > > = Use-cases = > > > > > > It seems to me like your proposed API doesn't really fit either one of > > > those usecases well... > > > > > > > Here are two known use-cases. The first one is “application kernel” > > > > sandboxes like User-mode Linux and gVisor. In this case, we have a > > > > process that runs the sandbox kernel and a set of stub processes that > > > > are used to manage guest address spaces. Guest code is executed in the > > > > context of stub processes but all system calls are intercepted and > > > > handled in the sandbox kernel. Right now, these sort of sandboxes use > > > > PTRACE_SYSEMU to trap system calls, but the process_vm_exec can > > > > significantly speed them up. > > > > > > In this case, since you really only want an mm_struct to run code > > > under, it seems weird to create a whole task with its own PID and so > > > on. It seems to me like something similar to the /dev/kvm API would be > > > more appropriate here? Implementation options that I see for that > > > would be: > > > > > > 1. mm_struct-based: > > > a set of syscalls to create a new mm_struct, > > > change memory mappings under that mm_struct, and switch to it > > > > I like the idea to have a handle for mm. Instead of pid, we will pass > > this handle to process_vm_exec. We have pidfd for processes and we can > > introduce mmfd for mm_struct. > > I personally think that it might be quite unwieldy when it comes to > the restrictions you get from trying to have shared memory with the > owning process - I'm having trouble figuring out how you can implement > copy-on-write semantics without relying on copy-on-write logic in the > host OS and without being able to use userfaultfd. It is easy. COW mappings are mapped to guest address spaces without the write permission. If one of processes wants to write something, it triggers a fault that is handled in the Sentry (supervisor/kernel). > > But if that's not a problem somehow, and you can find some reasonable > way to handle memory usage accounting and fix up everything that > assumes that multithreaded userspace threads don't switch ->mm, I > guess this might work for your usecase. > > > > 2. pagetable-mirroring-based: > > > like /dev/kvm, an API to create a new pagetable, mirror parts of > > > the mm_struct's pagetables over into it with modified permissions > > > (like KVM_SET_USER_MEMORY_REGION), > > > and run code under that context. > > > page fault handling would first handle the fault against mm->pgd > > > as normal, then mirror the PTE over into the secondary pagetables. > > > invalidation could be handled with MMU notifiers. > > > > > > > I found this idea interesting and decided to look at it more closely. > > After reading the kernel code for a few days, I realized that it would > > not be easy to implement something like this, > > Yeah, it might need architecture-specific code to flip the page tables > on userspace entry/exit, and maybe also for mirroring them. And for > the TLB flushing logic... > > > but more important is that > > I don’t understand what problem it solves. Will it simplify the > > user-space code? I don’t think so. Will it improve performance? It is > > unclear for me too. > > Some reasons I can think of are: > > - direct guest memory access: I imagined you'd probably want to be able to > directly access userspace memory from the supervisor, and > with this approach that'd become easy. Right now, we use shared memory regions for that and they work fine. As I already mentioned the most part of memory are never mapped to the supervisor address space. > > - integration with on-demand paging of the host OS: You'd be able to > create things like file-backed copy-on-write mappings from the > host filesystem, or implement your own mappings backed by some kind > of storage using userfaultfd. This isn't a problem either... > > - sandboxing: For sandboxing usecases (not your usecase), it would be > possible to e.g. create a read-only clone of the entire address space of a > process and give write access to specific parts of it, or something > like that. > These address space clones could potentially be created and destroyed > fairly quickly. This is a very valid example and I would assume this is where your idea was coming from. I have some doubts about the idea of additional sub-page-tables in the kernel, but I know a good way how to implement your idea with KVM. You can look at how the KVM platform is implemented in gVisor and this sort of sandboxing can be implemented in the same way. In a few words, we create a KVM virtual machine, repeat the process address space in the guest ring0, implement basic operating system-level stubs, so that the process can jump between the host ring3 and the guest ring0. https://github.com/google/gvisor/blob/master/pkg/ring0/ https://github.com/google/gvisor/tree/master/pkg/sentry/platform/kvm When we have all these bits, we can create any page tables for a guest ring3 and run untrusted code there. The sandbox process switches to the guest ring0 and then it switches to a guest ring3 with a specified page tables and a state. https://cs.opensource.google/gvisor/gvisor/+/master:pkg/sentry/platform/kvm/machine_amd64.go;l=356 With this scheme, the sandbox process will have direct access to page tables and will be able to change them. > > - accounting: memory usage would be automatically accounted to the > supervisor process, so even without a parasite process, you'd be able > to see the memory usage correctly in things like "top". > > - small (non-pageable) memory footprint in the host kernel: > The only things the host kernel would have to persistently store would be > the normal MM data structures for the supervisor plus the mappings > from "guest userspace" memory ranges to supervisor memory ranges; > userspace pagetables would be discardable, and could even be shared > with those of the supervisor in cases where the alignment fits. > So with this, large anonymous mappings with 4K granularity only cost you > ~0.20% overhead across host and guest address space; without this, if you > used shared mappings instead, you'd pay twice that for every 2MiB range > from which parts are accessed in both contexts, plus probably another > ~0.2% or so for the "struct address_space"? If we use shared mappings, we don't map the most part of guest memory to the supervisor address space and don't have page tables for it there. I would say that this is a question where a memory footprint will be smaller... > > - all memory-management-related syscalls could be directly performed > in the "kernel" process > > But yeah, some of those aren't really relevant for your usecase, and I > guess things like the accounting aspect could just as well be solved > differently... > > > First, in the KVM case, we have a few big linear mappings and need to > > support one “shadow” address space. In the case of sandboxes, we can > > have a tremendous amount of mappings and many address spaces that we > > need to manage. Memory mappings will be mapped with different addresses > > in a supervisor address space and “guest” address spaces. If guest > > address spaces will not have their mm_structs, we will need to reinvent > > vma-s in some form. If guest address spaces have mm_structs, this will > > look similar to https://lwn.net/Articles/830648/. > > > > Second, each pagetable is tied up with mm_stuct. You suggest creating > > new pagetables that will not have their mm_struct-s (sorry if I > > misunderstood something). > > Yeah, that's what I had in mind, page tables without an mm_struct. > > > I am not sure that it will be easy to > > implement. How many corner cases will be there? > > Yeah, it would require some work around TLB flushing and entry/exit > from userspace. But from a high-level perspective it feels to me like > a change with less systematic impact. Maybe I'm wrong about that. > > > As for page faults in a secondary address space, we will need to find a > > fault address in the main address space, handle the fault there and then > > mirror the PTE to the secondary pagetable. > > Right. > > > Effectively, it means that > > page faults will be handled in two address spaces. Right now, we use > > memfd and shared mappings. It means that each fault is handled only in > > one address space, and we map a guest memory region to the supervisor > > address space only when we need to access it. A large portion of guest > > anonymous memory is never mapped to the supervisor address space. > > Will an overhead of mirrored address spaces be smaller than memfd shared > > mappings? I am not sure. > > But as long as the mappings are sufficiently big and aligned properly, > or you explicitly manage the supervisor address space, some of that > cost disappears: E.g. even if a page is mapped in both address spaces, > you wouldn't have a memory cost for the second mapping if the page > tables are shared. You are right. It is interesting how many pte-s will be shared. For example, if a guest process forks a child, all anon memory will be COW, this means we will need to remove the W bit from pte-s and so we will need to allocate pte-s for both processes... > > > Third, this approach will not get rid of having process_vm_exec. We will > > need to switch to a guest address space with a specified state and > > switch back on faults or syscalls. > > Yeah, you'd still need a syscall for running code under a different > set of page tables. But that's something that KVM _almost_ already > does. I don't understand this analogy with KVM... > > > If the main concern is the ability to > > run syscalls on a remote mm, we can think about how to fix this. I see > > two ways what we can do here: > > > > * Specify the exact list of system calls that are allowed. The first > > three candidates are mmap, munmap, and vmsplice. > > > > * Instead of allowing us to run system calls, we can implement this in > > the form of commands. In the case of sandboxes, we need to implement > > only two commands to create and destroy memory mappings in a target > > address space. > > FWIW, there is precedent for something similar: The Android folks > already added process_madvise() for remotely messing with the VMAs of > another process to some degree. I know. We tried to implement process_vm_mmap and process_vm_splice: https://lkml.org/lkml/2018/1/9/32 https://patchwork.kernel.org/project/linux-mm/cover/155836064844.2441.10911127801797083064.stgit@localhost.localdomain/ Thanks, Andrei From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-pg1-x536.google.com ([2607:f8b0:4864:20::536]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1m4utD-007EYW-CP for linux-um@lists.infradead.org; Sun, 18 Jul 2021 00:42:41 +0000 Received: by mail-pg1-x536.google.com with SMTP id 70so11422336pgh.2 for ; Sat, 17 Jul 2021 17:42:38 -0700 (PDT) Date: Sat, 17 Jul 2021 17:38:52 -0700 From: Andrei Vagin Subject: Re: [PATCH 0/4 POC] Allow executing code and syscalls in another address space Message-ID: References: <20210414055217.543246-1-avagin@gmail.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Sender: "linux-um" Errors-To: linux-um-bounces+geert=linux-m68k.org@lists.infradead.org To: Jann Horn Cc: kernel list , Linux API , linux-um@lists.infradead.org, criu@openvz.org, avagin@google.com, Andrew Morton , Andy Lutomirski , Anton Ivanov , Christian Brauner , Dmitry Safonov <0x7f454c46@gmail.com>, Ingo Molnar , Jeff Dike , Mike Rapoport , Michael Kerrisk , Oleg Nesterov , Peter Zijlstra , Richard Weinberger , Thomas Gleixner , linux-mm@kvack.org T24gRnJpLCBKdWwgMDIsIDIwMjEgYXQgMDU6MTI6MDJQTSArMDIwMCwgSmFubiBIb3JuIHdyb3Rl Ogo+IE9uIEZyaSwgSnVsIDIsIDIwMjEgYXQgOTowMSBBTSBBbmRyZWkgVmFnaW4gPGF2YWdpbkBn bWFpbC5jb20+IHdyb3RlOgo+ID4gT24gV2VkLCBBcHIgMTQsIDIwMjEgYXQgMDg6NDY6NDBBTSAr MDIwMCwgSmFubiBIb3JuIHdyb3RlOgo+ID4gPiBPbiBXZWQsIEFwciAxNCwgMjAyMSBhdCA3OjU5 IEFNIEFuZHJlaSBWYWdpbiA8YXZhZ2luQGdtYWlsLmNvbT4gd3JvdGU6Cj4gPiA+ID4gV2UgYWxy ZWFkeSBoYXZlIHByb2Nlc3Nfdm1fcmVhZHYgYW5kIHByb2Nlc3Nfdm1fd3JpdGV2IHRvIHJlYWQg YW5kIHdyaXRlCj4gPiA+ID4gdG8gYSBwcm9jZXNzIG1lbW9yeSBmYXN0ZXIgdGhhbiB3ZSBjYW4g ZG8gdGhpcyB3aXRoIHB0cmFjZS4gQW5kIG5vdyBpdAo+ID4gPiA+IGlzIHRpbWUgZm9yIHByb2Nl c3Nfdm1fZXhlYyB0aGF0IGFsbG93cyBleGVjdXRpbmcgY29kZSBpbiBhbiBhZGRyZXNzCj4gPiA+ ID4gc3BhY2Ugb2YgYW5vdGhlciBwcm9jZXNzLiBXZSBjYW4gZG8gdGhpcyB3aXRoIHB0cmFjZSBi dXQgaXQgaXMgbXVjaAo+ID4gPiA+IHNsb3dlci4KPiA+ID4gPgo+ID4gPiA+ID0gVXNlLWNhc2Vz ID0KPiA+ID4KPiA+ID4gSXQgc2VlbXMgdG8gbWUgbGlrZSB5b3VyIHByb3Bvc2VkIEFQSSBkb2Vz bid0IHJlYWxseSBmaXQgZWl0aGVyIG9uZSBvZgo+ID4gPiB0aG9zZSB1c2VjYXNlcyB3ZWxsLi4u Cj4gPiA+Cj4gPiA+ID4gSGVyZSBhcmUgdHdvIGtub3duIHVzZS1jYXNlcy4gVGhlIGZpcnN0IG9u ZSBpcyDigJxhcHBsaWNhdGlvbiBrZXJuZWzigJ0KPiA+ID4gPiBzYW5kYm94ZXMgbGlrZSBVc2Vy LW1vZGUgTGludXggYW5kIGdWaXNvci4gSW4gdGhpcyBjYXNlLCB3ZSBoYXZlIGEKPiA+ID4gPiBw cm9jZXNzIHRoYXQgcnVucyB0aGUgc2FuZGJveCBrZXJuZWwgYW5kIGEgc2V0IG9mIHN0dWIgcHJv Y2Vzc2VzIHRoYXQKPiA+ID4gPiBhcmUgdXNlZCB0byBtYW5hZ2UgZ3Vlc3QgYWRkcmVzcyBzcGFj ZXMuIEd1ZXN0IGNvZGUgaXMgZXhlY3V0ZWQgaW4gdGhlCj4gPiA+ID4gY29udGV4dCBvZiBzdHVi IHByb2Nlc3NlcyBidXQgYWxsIHN5c3RlbSBjYWxscyBhcmUgaW50ZXJjZXB0ZWQgYW5kCj4gPiA+ ID4gaGFuZGxlZCBpbiB0aGUgc2FuZGJveCBrZXJuZWwuIFJpZ2h0IG5vdywgdGhlc2Ugc29ydCBv ZiBzYW5kYm94ZXMgdXNlCj4gPiA+ID4gUFRSQUNFX1NZU0VNVSB0byB0cmFwIHN5c3RlbSBjYWxs cywgYnV0IHRoZSBwcm9jZXNzX3ZtX2V4ZWMgY2FuCj4gPiA+ID4gc2lnbmlmaWNhbnRseSBzcGVl ZCB0aGVtIHVwLgo+ID4gPgo+ID4gPiBJbiB0aGlzIGNhc2UsIHNpbmNlIHlvdSByZWFsbHkgb25s eSB3YW50IGFuIG1tX3N0cnVjdCB0byBydW4gY29kZQo+ID4gPiB1bmRlciwgaXQgc2VlbXMgd2Vp cmQgdG8gY3JlYXRlIGEgd2hvbGUgdGFzayB3aXRoIGl0cyBvd24gUElEIGFuZCBzbwo+ID4gPiBv bi4gSXQgc2VlbXMgdG8gbWUgbGlrZSBzb21ldGhpbmcgc2ltaWxhciB0byB0aGUgL2Rldi9rdm0g QVBJIHdvdWxkIGJlCj4gPiA+IG1vcmUgYXBwcm9wcmlhdGUgaGVyZT8gSW1wbGVtZW50YXRpb24g b3B0aW9ucyB0aGF0IEkgc2VlIGZvciB0aGF0Cj4gPiA+IHdvdWxkIGJlOgo+ID4gPgo+ID4gPiAx LiBtbV9zdHJ1Y3QtYmFzZWQ6Cj4gPiA+ICAgICAgIGEgc2V0IG9mIHN5c2NhbGxzIHRvIGNyZWF0 ZSBhIG5ldyBtbV9zdHJ1Y3QsCj4gPiA+ICAgICAgIGNoYW5nZSBtZW1vcnkgbWFwcGluZ3MgdW5k ZXIgdGhhdCBtbV9zdHJ1Y3QsIGFuZCBzd2l0Y2ggdG8gaXQKPiA+Cj4gPiBJIGxpa2UgdGhlIGlk ZWEgdG8gaGF2ZSBhIGhhbmRsZSBmb3IgbW0uIEluc3RlYWQgb2YgcGlkLCB3ZSB3aWxsIHBhc3MK PiA+IHRoaXMgaGFuZGxlIHRvIHByb2Nlc3Nfdm1fZXhlYy4gV2UgaGF2ZSBwaWRmZCBmb3IgcHJv Y2Vzc2VzIGFuZCB3ZSBjYW4KPiA+IGludHJvZHVjZSBtbWZkIGZvciBtbV9zdHJ1Y3QuCj4gCj4g SSBwZXJzb25hbGx5IHRoaW5rIHRoYXQgaXQgbWlnaHQgYmUgcXVpdGUgdW53aWVsZHkgd2hlbiBp dCBjb21lcyB0bwo+IHRoZSByZXN0cmljdGlvbnMgeW91IGdldCBmcm9tIHRyeWluZyB0byBoYXZl IHNoYXJlZCBtZW1vcnkgd2l0aCB0aGUKPiBvd25pbmcgcHJvY2VzcyAtIEknbSBoYXZpbmcgdHJv dWJsZSBmaWd1cmluZyBvdXQgaG93IHlvdSBjYW4gaW1wbGVtZW50Cj4gY29weS1vbi13cml0ZSBz ZW1hbnRpY3Mgd2l0aG91dCByZWx5aW5nIG9uIGNvcHktb24td3JpdGUgbG9naWMgaW4gdGhlCj4g aG9zdCBPUyBhbmQgd2l0aG91dCBiZWluZyBhYmxlIHRvIHVzZSB1c2VyZmF1bHRmZC4KCkl0IGlz IGVhc3kuIENPVyBtYXBwaW5ncyBhcmUgbWFwcGVkIHRvIGd1ZXN0IGFkZHJlc3Mgc3BhY2VzIHdp dGhvdXQgdGhlCndyaXRlIHBlcm1pc3Npb24uIElmIG9uZSBvZiBwcm9jZXNzZXMgd2FudHMgdG8g d3JpdGUgc29tZXRoaW5nLCBpdAp0cmlnZ2VycyBhIGZhdWx0IHRoYXQgaXMgaGFuZGxlZCBpbiB0 aGUgU2VudHJ5IChzdXBlcnZpc29yL2tlcm5lbCkuCgo+IAo+IEJ1dCBpZiB0aGF0J3Mgbm90IGEg cHJvYmxlbSBzb21laG93LCBhbmQgeW91IGNhbiBmaW5kIHNvbWUgcmVhc29uYWJsZQo+IHdheSB0 byBoYW5kbGUgbWVtb3J5IHVzYWdlIGFjY291bnRpbmcgYW5kIGZpeCB1cCBldmVyeXRoaW5nIHRo YXQKPiBhc3N1bWVzIHRoYXQgbXVsdGl0aHJlYWRlZCB1c2Vyc3BhY2UgdGhyZWFkcyBkb24ndCBz d2l0Y2ggLT5tbSwgSQo+IGd1ZXNzIHRoaXMgbWlnaHQgd29yayBmb3IgeW91ciB1c2VjYXNlLgo+ IAo+ID4gPiAyLiBwYWdldGFibGUtbWlycm9yaW5nLWJhc2VkOgo+ID4gPiAgICAgICBsaWtlIC9k ZXYva3ZtLCBhbiBBUEkgdG8gY3JlYXRlIGEgbmV3IHBhZ2V0YWJsZSwgbWlycm9yIHBhcnRzIG9m Cj4gPiA+ICAgICAgIHRoZSBtbV9zdHJ1Y3QncyBwYWdldGFibGVzIG92ZXIgaW50byBpdCB3aXRo IG1vZGlmaWVkIHBlcm1pc3Npb25zCj4gPiA+ICAgICAgIChsaWtlIEtWTV9TRVRfVVNFUl9NRU1P UllfUkVHSU9OKSwKPiA+ID4gICAgICAgYW5kIHJ1biBjb2RlIHVuZGVyIHRoYXQgY29udGV4dC4K PiA+ID4gICAgICAgcGFnZSBmYXVsdCBoYW5kbGluZyB3b3VsZCBmaXJzdCBoYW5kbGUgdGhlIGZh dWx0IGFnYWluc3QgbW0tPnBnZAo+ID4gPiAgICAgICBhcyBub3JtYWwsIHRoZW4gbWlycm9yIHRo ZSBQVEUgb3ZlciBpbnRvIHRoZSBzZWNvbmRhcnkgcGFnZXRhYmxlcy4KPiA+ID4gICAgICAgaW52 YWxpZGF0aW9uIGNvdWxkIGJlIGhhbmRsZWQgd2l0aCBNTVUgbm90aWZpZXJzLgo+ID4gPgo+ID4K PiA+IEkgZm91bmQgdGhpcyBpZGVhIGludGVyZXN0aW5nIGFuZCBkZWNpZGVkIHRvIGxvb2sgYXQg aXQgbW9yZSBjbG9zZWx5Lgo+ID4gQWZ0ZXIgcmVhZGluZyB0aGUga2VybmVsIGNvZGUgZm9yIGEg ZmV3IGRheXMsIEkgcmVhbGl6ZWQgdGhhdCBpdCB3b3VsZAo+ID4gbm90IGJlIGVhc3kgdG8gaW1w bGVtZW50IHNvbWV0aGluZyBsaWtlIHRoaXMsCj4gCj4gWWVhaCwgaXQgbWlnaHQgbmVlZCBhcmNo aXRlY3R1cmUtc3BlY2lmaWMgY29kZSB0byBmbGlwIHRoZSBwYWdlIHRhYmxlcwo+IG9uIHVzZXJz cGFjZSBlbnRyeS9leGl0LCBhbmQgbWF5YmUgYWxzbyBmb3IgbWlycm9yaW5nIHRoZW0uIEFuZCBm b3IKPiB0aGUgVExCIGZsdXNoaW5nIGxvZ2ljLi4uCj4gCj4gPiBidXQgbW9yZSBpbXBvcnRhbnQg aXMgdGhhdAo+ID4gSSBkb27igJl0IHVuZGVyc3RhbmQgd2hhdCBwcm9ibGVtIGl0IHNvbHZlcy4g V2lsbCBpdCBzaW1wbGlmeSB0aGUKPiA+IHVzZXItc3BhY2UgY29kZT8gSSBkb27igJl0IHRoaW5r IHNvLiBXaWxsIGl0IGltcHJvdmUgcGVyZm9ybWFuY2U/IEl0IGlzCj4gPiB1bmNsZWFyIGZvciBt ZSB0b28uCj4gCj4gU29tZSByZWFzb25zIEkgY2FuIHRoaW5rIG9mIGFyZToKPiAKPiAgLSBkaXJl Y3QgZ3Vlc3QgbWVtb3J5IGFjY2VzczogSSBpbWFnaW5lZCB5b3UnZCBwcm9iYWJseSB3YW50IHRv IGJlIGFibGUgdG8KPiAgICBkaXJlY3RseSBhY2Nlc3MgdXNlcnNwYWNlIG1lbW9yeSBmcm9tIHRo ZSBzdXBlcnZpc29yLCBhbmQKPiAgICB3aXRoIHRoaXMgYXBwcm9hY2ggdGhhdCdkIGJlY29tZSBl YXN5LgoKUmlnaHQgbm93LCB3ZSB1c2Ugc2hhcmVkIG1lbW9yeSByZWdpb25zIGZvciB0aGF0IGFu ZCB0aGV5IHdvcmsgZmluZS4gQXMKSSBhbHJlYWR5IG1lbnRpb25lZCB0aGUgbW9zdCBwYXJ0IG9m IG1lbW9yeSBhcmUgbmV2ZXIgbWFwcGVkIHRvIHRoZQpzdXBlcnZpc29yIGFkZHJlc3Mgc3BhY2Uu Cgo+IAo+ICAtIGludGVncmF0aW9uIHdpdGggb24tZGVtYW5kIHBhZ2luZyBvZiB0aGUgaG9zdCBP UzogWW91J2QgYmUgYWJsZSB0bwo+ICAgIGNyZWF0ZSB0aGluZ3MgbGlrZSBmaWxlLWJhY2tlZCBj b3B5LW9uLXdyaXRlIG1hcHBpbmdzIGZyb20gdGhlCj4gICAgaG9zdCBmaWxlc3lzdGVtLCBvciBp bXBsZW1lbnQgeW91ciBvd24gbWFwcGluZ3MgYmFja2VkIGJ5IHNvbWUga2luZAo+ICAgIG9mIHN0 b3JhZ2UgdXNpbmcgdXNlcmZhdWx0ZmQuCgpUaGlzIGlzbid0IGEgcHJvYmxlbSBlaXRoZXIuLi4K Cj4gCj4gIC0gc2FuZGJveGluZzogRm9yIHNhbmRib3hpbmcgdXNlY2FzZXMgKG5vdCB5b3VyIHVz ZWNhc2UpLCBpdCB3b3VsZCBiZQo+ICAgIHBvc3NpYmxlIHRvIGUuZy4gY3JlYXRlIGEgcmVhZC1v bmx5IGNsb25lIG9mIHRoZSBlbnRpcmUgYWRkcmVzcyBzcGFjZSBvZiBhCj4gICAgcHJvY2VzcyBh bmQgZ2l2ZSB3cml0ZSBhY2Nlc3MgdG8gc3BlY2lmaWMgcGFydHMgb2YgaXQsIG9yIHNvbWV0aGlu Zwo+ICAgIGxpa2UgdGhhdC4KPiAgICBUaGVzZSBhZGRyZXNzIHNwYWNlIGNsb25lcyBjb3VsZCBw b3RlbnRpYWxseSBiZSBjcmVhdGVkIGFuZCBkZXN0cm95ZWQKPiAgICBmYWlybHkgcXVpY2tseS4K ClRoaXMgaXMgYSB2ZXJ5IHZhbGlkIGV4YW1wbGUgYW5kIEkgd291bGQgYXNzdW1lIHRoaXMgaXMg d2hlcmUgeW91ciBpZGVhCndhcyBjb21pbmcgZnJvbS4gSSBoYXZlIHNvbWUgZG91YnRzIGFib3V0 IHRoZSBpZGVhIG9mIGFkZGl0aW9uYWwKc3ViLXBhZ2UtdGFibGVzIGluIHRoZSBrZXJuZWwsIGJ1 dCBJIGtub3cgYSBnb29kIHdheSBob3cgdG8gaW1wbGVtZW50CnlvdXIgaWRlYSB3aXRoIEtWTS4g WW91IGNhbiBsb29rIGF0IGhvdyB0aGUgS1ZNIHBsYXRmb3JtIGlzIGltcGxlbWVudGVkIGluCmdW aXNvciBhbmQgdGhpcyBzb3J0IG9mIHNhbmRib3hpbmcgY2FuIGJlIGltcGxlbWVudGVkIGluIHRo ZSBzYW1lIHdheS4KCkluIGEgZmV3IHdvcmRzLCB3ZSBjcmVhdGUgYSBLVk0gdmlydHVhbCBtYWNo aW5lLCByZXBlYXQgdGhlIHByb2Nlc3MKYWRkcmVzcyBzcGFjZSBpbiB0aGUgZ3Vlc3QgcmluZzAs IGltcGxlbWVudCBiYXNpYyBvcGVyYXRpbmcgc3lzdGVtLWxldmVsCnN0dWJzLCBzbyB0aGF0IHRo ZSBwcm9jZXNzIGNhbiBqdW1wIGJldHdlZW4gdGhlIGhvc3QgcmluZzMgYW5kIHRoZSBndWVzdApy aW5nMC4KCmh0dHBzOi8vZ2l0aHViLmNvbS9nb29nbGUvZ3Zpc29yL2Jsb2IvbWFzdGVyL3BrZy9y aW5nMC8KaHR0cHM6Ly9naXRodWIuY29tL2dvb2dsZS9ndmlzb3IvdHJlZS9tYXN0ZXIvcGtnL3Nl bnRyeS9wbGF0Zm9ybS9rdm0KCldoZW4gd2UgaGF2ZSBhbGwgdGhlc2UgYml0cywgd2UgY2FuIGNy ZWF0ZSBhbnkgcGFnZSB0YWJsZXMgZm9yIGEgZ3Vlc3QKcmluZzMgYW5kIHJ1biB1bnRydXN0ZWQg Y29kZSB0aGVyZS4gVGhlIHNhbmRib3ggcHJvY2VzcyBzd2l0Y2hlcyB0bwp0aGUgZ3Vlc3Qgcmlu ZzAgYW5kIHRoZW4gaXQgc3dpdGNoZXMgdG8gYSBndWVzdCByaW5nMyB3aXRoIGEgc3BlY2lmaWVk CnBhZ2UgdGFibGVzIGFuZCBhIHN0YXRlLgoKaHR0cHM6Ly9jcy5vcGVuc291cmNlLmdvb2dsZS9n dmlzb3IvZ3Zpc29yLysvbWFzdGVyOnBrZy9zZW50cnkvcGxhdGZvcm0va3ZtL21hY2hpbmVfYW1k NjQuZ287bD0zNTYKCldpdGggdGhpcyBzY2hlbWUsIHRoZSBzYW5kYm94IHByb2Nlc3Mgd2lsbCBo YXZlIGRpcmVjdCBhY2Nlc3MgdG8gcGFnZQp0YWJsZXMgYW5kIHdpbGwgYmUgYWJsZSB0byBjaGFu Z2UgdGhlbS4KCj4gCj4gIC0gYWNjb3VudGluZzogbWVtb3J5IHVzYWdlIHdvdWxkIGJlIGF1dG9t YXRpY2FsbHkgYWNjb3VudGVkIHRvIHRoZQo+ICAgIHN1cGVydmlzb3IgcHJvY2Vzcywgc28gZXZl biB3aXRob3V0IGEgcGFyYXNpdGUgcHJvY2VzcywgeW91J2QgYmUgYWJsZQo+ICAgIHRvIHNlZSB0 aGUgbWVtb3J5IHVzYWdlIGNvcnJlY3RseSBpbiB0aGluZ3MgbGlrZSAidG9wIi4KPiAKPiAgLSBz bWFsbCAobm9uLXBhZ2VhYmxlKSBtZW1vcnkgZm9vdHByaW50IGluIHRoZSBob3N0IGtlcm5lbDoK PiAgICBUaGUgb25seSB0aGluZ3MgdGhlIGhvc3Qga2VybmVsIHdvdWxkIGhhdmUgdG8gcGVyc2lz dGVudGx5IHN0b3JlIHdvdWxkIGJlCj4gICAgdGhlIG5vcm1hbCBNTSBkYXRhIHN0cnVjdHVyZXMg Zm9yIHRoZSBzdXBlcnZpc29yIHBsdXMgdGhlIG1hcHBpbmdzCj4gICAgZnJvbSAiZ3Vlc3QgdXNl cnNwYWNlIiBtZW1vcnkgcmFuZ2VzIHRvIHN1cGVydmlzb3IgbWVtb3J5IHJhbmdlczsKPiAgICB1 c2Vyc3BhY2UgcGFnZXRhYmxlcyB3b3VsZCBiZSBkaXNjYXJkYWJsZSwgYW5kIGNvdWxkIGV2ZW4g YmUgc2hhcmVkCj4gICAgd2l0aCB0aG9zZSBvZiB0aGUgc3VwZXJ2aXNvciBpbiBjYXNlcyB3aGVy ZSB0aGUgYWxpZ25tZW50IGZpdHMuCj4gICAgU28gd2l0aCB0aGlzLCBsYXJnZSBhbm9ueW1vdXMg bWFwcGluZ3Mgd2l0aCA0SyBncmFudWxhcml0eSBvbmx5IGNvc3QgeW91Cj4gICAgfjAuMjAlIG92 ZXJoZWFkIGFjcm9zcyBob3N0IGFuZCBndWVzdCBhZGRyZXNzIHNwYWNlOyB3aXRob3V0IHRoaXMs IGlmIHlvdQo+ICAgIHVzZWQgc2hhcmVkIG1hcHBpbmdzIGluc3RlYWQsIHlvdSdkIHBheSB0d2lj ZSB0aGF0IGZvciBldmVyeSAyTWlCIHJhbmdlCj4gICAgZnJvbSB3aGljaCBwYXJ0cyBhcmUgYWNj ZXNzZWQgaW4gYm90aCBjb250ZXh0cywgcGx1cyBwcm9iYWJseSBhbm90aGVyCj4gICAgfjAuMiUg b3Igc28gZm9yIHRoZSAic3RydWN0IGFkZHJlc3Nfc3BhY2UiPwoKSWYgd2UgdXNlIHNoYXJlZCBt YXBwaW5ncywgd2UgZG9uJ3QgbWFwIHRoZSBtb3N0IHBhcnQgb2YgZ3Vlc3QgbWVtb3J5IHRvCnRo ZSBzdXBlcnZpc29yIGFkZHJlc3Mgc3BhY2UgYW5kIGRvbid0IGhhdmUgcGFnZSB0YWJsZXMgZm9y IGl0IHRoZXJlLiBJCndvdWxkIHNheSB0aGF0IHRoaXMgaXMgYSBxdWVzdGlvbiB3aGVyZSBhIG1l bW9yeSBmb290cHJpbnQgd2lsbCBiZQpzbWFsbGVyLi4uCgo+IAo+ICAtIGFsbCBtZW1vcnktbWFu YWdlbWVudC1yZWxhdGVkIHN5c2NhbGxzIGNvdWxkIGJlIGRpcmVjdGx5IHBlcmZvcm1lZAo+ICAg IGluIHRoZSAia2VybmVsIiBwcm9jZXNzCj4gCj4gQnV0IHllYWgsIHNvbWUgb2YgdGhvc2UgYXJl bid0IHJlYWxseSByZWxldmFudCBmb3IgeW91ciB1c2VjYXNlLCBhbmQgSQo+IGd1ZXNzIHRoaW5n cyBsaWtlIHRoZSBhY2NvdW50aW5nIGFzcGVjdCBjb3VsZCBqdXN0IGFzIHdlbGwgYmUgc29sdmVk Cj4gZGlmZmVyZW50bHkuLi4KPiAKPiA+IEZpcnN0LCBpbiB0aGUgS1ZNIGNhc2UsIHdlIGhhdmUg YSBmZXcgYmlnIGxpbmVhciBtYXBwaW5ncyBhbmQgbmVlZCB0bwo+ID4gc3VwcG9ydCBvbmUg4oCc c2hhZG934oCdIGFkZHJlc3Mgc3BhY2UuIEluIHRoZSBjYXNlIG9mIHNhbmRib3hlcywgd2UgY2Fu Cj4gPiBoYXZlIGEgdHJlbWVuZG91cyBhbW91bnQgb2YgbWFwcGluZ3MgYW5kIG1hbnkgYWRkcmVz cyBzcGFjZXMgdGhhdCB3ZQo+ID4gbmVlZCB0byBtYW5hZ2UuICBNZW1vcnkgbWFwcGluZ3Mgd2ls bCBiZSBtYXBwZWQgd2l0aCBkaWZmZXJlbnQgYWRkcmVzc2VzCj4gPiBpbiBhIHN1cGVydmlzb3Ig YWRkcmVzcyBzcGFjZSBhbmQg4oCcZ3Vlc3TigJ0gYWRkcmVzcyBzcGFjZXMuIElmIGd1ZXN0Cj4g PiBhZGRyZXNzIHNwYWNlcyB3aWxsIG5vdCBoYXZlIHRoZWlyIG1tX3N0cnVjdHMsIHdlIHdpbGwg bmVlZCB0byByZWludmVudAo+ID4gdm1hLXMgaW4gc29tZSBmb3JtLiBJZiBndWVzdCBhZGRyZXNz IHNwYWNlcyBoYXZlIG1tX3N0cnVjdHMsIHRoaXMgd2lsbAo+ID4gbG9vayBzaW1pbGFyIHRvIGh0 dHBzOi8vbHduLm5ldC9BcnRpY2xlcy84MzA2NDgvLgo+ID4KPiA+IFNlY29uZCwgZWFjaCBwYWdl dGFibGUgaXMgdGllZCB1cCB3aXRoIG1tX3N0dWN0LiBZb3Ugc3VnZ2VzdCBjcmVhdGluZwo+ID4g bmV3IHBhZ2V0YWJsZXMgdGhhdCB3aWxsIG5vdCBoYXZlIHRoZWlyIG1tX3N0cnVjdC1zIChzb3Jy eSBpZiBJCj4gPiBtaXN1bmRlcnN0b29kIHNvbWV0aGluZykuCj4gCj4gWWVhaCwgdGhhdCdzIHdo YXQgSSBoYWQgaW4gbWluZCwgcGFnZSB0YWJsZXMgd2l0aG91dCBhbiBtbV9zdHJ1Y3QuCj4gCj4g PiBJIGFtIG5vdCBzdXJlIHRoYXQgaXQgd2lsbCBiZSBlYXN5IHRvCj4gPiBpbXBsZW1lbnQuIEhv dyBtYW55IGNvcm5lciBjYXNlcyB3aWxsIGJlIHRoZXJlPwo+IAo+IFllYWgsIGl0IHdvdWxkIHJl cXVpcmUgc29tZSB3b3JrIGFyb3VuZCBUTEIgZmx1c2hpbmcgYW5kIGVudHJ5L2V4aXQKPiBmcm9t IHVzZXJzcGFjZS4gQnV0IGZyb20gYSBoaWdoLWxldmVsIHBlcnNwZWN0aXZlIGl0IGZlZWxzIHRv IG1lIGxpa2UKPiBhIGNoYW5nZSB3aXRoIGxlc3Mgc3lzdGVtYXRpYyBpbXBhY3QuIE1heWJlIEkn bSB3cm9uZyBhYm91dCB0aGF0Lgo+IAo+ID4gQXMgZm9yIHBhZ2UgZmF1bHRzIGluIGEgc2Vjb25k YXJ5IGFkZHJlc3Mgc3BhY2UsIHdlIHdpbGwgbmVlZCB0byBmaW5kIGEKPiA+IGZhdWx0IGFkZHJl c3MgaW4gdGhlIG1haW4gYWRkcmVzcyBzcGFjZSwgaGFuZGxlIHRoZSBmYXVsdCB0aGVyZSBhbmQg dGhlbgo+ID4gbWlycm9yIHRoZSBQVEUgdG8gdGhlIHNlY29uZGFyeSBwYWdldGFibGUuCj4gCj4g UmlnaHQuCj4gCj4gPiBFZmZlY3RpdmVseSwgaXQgbWVhbnMgdGhhdAo+ID4gcGFnZSBmYXVsdHMg d2lsbCBiZSBoYW5kbGVkIGluIHR3byBhZGRyZXNzIHNwYWNlcy4gUmlnaHQgbm93LCB3ZSB1c2UK PiA+IG1lbWZkIGFuZCBzaGFyZWQgbWFwcGluZ3MuIEl0IG1lYW5zIHRoYXQgZWFjaCBmYXVsdCBp cyBoYW5kbGVkIG9ubHkgaW4KPiA+IG9uZSBhZGRyZXNzIHNwYWNlLCBhbmQgd2UgbWFwIGEgZ3Vl c3QgbWVtb3J5IHJlZ2lvbiB0byB0aGUgc3VwZXJ2aXNvcgo+ID4gYWRkcmVzcyBzcGFjZSBvbmx5 IHdoZW4gd2UgbmVlZCB0byBhY2Nlc3MgaXQuIEEgbGFyZ2UgcG9ydGlvbiBvZiBndWVzdAo+ID4g YW5vbnltb3VzIG1lbW9yeSBpcyBuZXZlciBtYXBwZWQgdG8gdGhlIHN1cGVydmlzb3IgYWRkcmVz cyBzcGFjZS4KPiA+IFdpbGwgYW4gb3ZlcmhlYWQgb2YgbWlycm9yZWQgYWRkcmVzcyBzcGFjZXMg YmUgc21hbGxlciB0aGFuIG1lbWZkIHNoYXJlZAo+ID4gbWFwcGluZ3M/IEkgYW0gbm90IHN1cmUu Cj4gCj4gQnV0IGFzIGxvbmcgYXMgdGhlIG1hcHBpbmdzIGFyZSBzdWZmaWNpZW50bHkgYmlnIGFu ZCBhbGlnbmVkIHByb3Blcmx5LAo+IG9yIHlvdSBleHBsaWNpdGx5IG1hbmFnZSB0aGUgc3VwZXJ2 aXNvciBhZGRyZXNzIHNwYWNlLCBzb21lIG9mIHRoYXQKPiBjb3N0IGRpc2FwcGVhcnM6IEUuZy4g ZXZlbiBpZiBhIHBhZ2UgaXMgbWFwcGVkIGluIGJvdGggYWRkcmVzcyBzcGFjZXMsCj4geW91IHdv dWxkbid0IGhhdmUgYSBtZW1vcnkgY29zdCBmb3IgdGhlIHNlY29uZCBtYXBwaW5nIGlmIHRoZSBw YWdlCj4gdGFibGVzIGFyZSBzaGFyZWQuCgpZb3UgYXJlIHJpZ2h0LiBJdCBpcyBpbnRlcmVzdGlu ZyBob3cgbWFueSBwdGUtcyB3aWxsIGJlIHNoYXJlZC4gRm9yCmV4YW1wbGUsIGlmIGEgZ3Vlc3Qg cHJvY2VzcyBmb3JrcyBhIGNoaWxkLCBhbGwgYW5vbiBtZW1vcnkgd2lsbCBiZSBDT1csCnRoaXMg bWVhbnMgd2Ugd2lsbCBuZWVkIHRvIHJlbW92ZSB0aGUgVyBiaXQgZnJvbSBwdGUtcyBhbmQgc28g d2Ugd2lsbApuZWVkIHRvIGFsbG9jYXRlIHB0ZS1zIGZvciBib3RoIHByb2Nlc3Nlcy4uLgoKPiAK PiA+IFRoaXJkLCB0aGlzIGFwcHJvYWNoIHdpbGwgbm90IGdldCByaWQgb2YgaGF2aW5nIHByb2Nl c3Nfdm1fZXhlYy4gV2Ugd2lsbAo+ID4gbmVlZCB0byBzd2l0Y2ggdG8gYSBndWVzdCBhZGRyZXNz IHNwYWNlIHdpdGggYSBzcGVjaWZpZWQgc3RhdGUgYW5kCj4gPiBzd2l0Y2ggYmFjayBvbiBmYXVs dHMgb3Igc3lzY2FsbHMuCj4gCj4gWWVhaCwgeW91J2Qgc3RpbGwgbmVlZCBhIHN5c2NhbGwgZm9y IHJ1bm5pbmcgY29kZSB1bmRlciBhIGRpZmZlcmVudAo+IHNldCBvZiBwYWdlIHRhYmxlcy4gQnV0 IHRoYXQncyBzb21ldGhpbmcgdGhhdCBLVk0gX2FsbW9zdF8gYWxyZWFkeQo+IGRvZXMuCgpJIGRv bid0IHVuZGVyc3RhbmQgdGhpcyBhbmFsb2d5IHdpdGggS1ZNLi4uCgo+IAo+ID4gSWYgdGhlIG1h aW4gY29uY2VybiBpcyB0aGUgYWJpbGl0eSB0bwo+ID4gcnVuIHN5c2NhbGxzIG9uIGEgcmVtb3Rl IG1tLCB3ZSBjYW4gdGhpbmsgYWJvdXQgaG93IHRvIGZpeCB0aGlzLiBJIHNlZQo+ID4gdHdvIHdh eXMgd2hhdCB3ZSBjYW4gZG8gaGVyZToKPiA+Cj4gPiAqIFNwZWNpZnkgdGhlIGV4YWN0IGxpc3Qg b2Ygc3lzdGVtIGNhbGxzIHRoYXQgYXJlIGFsbG93ZWQuIFRoZSBmaXJzdAo+ID4gdGhyZWUgY2Fu ZGlkYXRlcyBhcmUgbW1hcCwgbXVubWFwLCBhbmQgdm1zcGxpY2UuCj4gPgo+ID4gKiBJbnN0ZWFk IG9mIGFsbG93aW5nIHVzIHRvIHJ1biBzeXN0ZW0gY2FsbHMsIHdlIGNhbiBpbXBsZW1lbnQgdGhp cyBpbgo+ID4gdGhlIGZvcm0gb2YgY29tbWFuZHMuIEluIHRoZSBjYXNlIG9mIHNhbmRib3hlcywg d2UgbmVlZCB0byBpbXBsZW1lbnQKPiA+IG9ubHkgdHdvIGNvbW1hbmRzIHRvIGNyZWF0ZSBhbmQg ZGVzdHJveSBtZW1vcnkgbWFwcGluZ3MgaW4gYSB0YXJnZXQKPiA+IGFkZHJlc3Mgc3BhY2UuCj4g Cj4gRldJVywgdGhlcmUgaXMgcHJlY2VkZW50IGZvciBzb21ldGhpbmcgc2ltaWxhcjogVGhlIEFu ZHJvaWQgZm9sa3MKPiBhbHJlYWR5IGFkZGVkIHByb2Nlc3NfbWFkdmlzZSgpIGZvciByZW1vdGVs eSBtZXNzaW5nIHdpdGggdGhlIFZNQXMgb2YKPiBhbm90aGVyIHByb2Nlc3MgdG8gc29tZSBkZWdy ZWUuCgpJIGtub3cuIFdlIHRyaWVkIHRvIGltcGxlbWVudCBwcm9jZXNzX3ZtX21tYXAgYW5kIHBy b2Nlc3Nfdm1fc3BsaWNlOgoKaHR0cHM6Ly9sa21sLm9yZy9sa21sLzIwMTgvMS85LzMyCmh0dHBz Oi8vcGF0Y2h3b3JrLmtlcm5lbC5vcmcvcHJvamVjdC9saW51eC1tbS9jb3Zlci8xNTU4MzYwNjQ4 NDQuMjQ0MS4xMDkxMTEyNzgwMTc5NzA4MzA2NC5zdGdpdEBsb2NhbGhvc3QubG9jYWxkb21haW4v CgpUaGFua3MsCkFuZHJlaQoKX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f X19fX19fX18KbGludXgtdW0gbWFpbGluZyBsaXN0CmxpbnV4LXVtQGxpc3RzLmluZnJhZGVhZC5v cmcKaHR0cDovL2xpc3RzLmluZnJhZGVhZC5vcmcvbWFpbG1hbi9saXN0aW5mby9saW51eC11bQo=