Build scatterlist covering a process' text segment?

Arvid Brodin arvid.brodin at enea.com
Thu Jun 16 20:47:25 EDT 2011


Mulyadi Santosa wrote:
> Hi...
> 
> On Sat, May 28, 2011 at 04:58, Arvid Brodin <arvid.brodin at enea.com> wrote:
>> Ok. And looking at e.g. sg_set_buf(), the scatterlist expects a kernel virtual
>> address (it uses virt_to_page() on its "buf" parameter internally, which
>> requires a kernel virtual adress, if I understand correctly).
>>
>> There seems to be no way to map process adresses to kernel addresses. (Well I
>> guess one could follow the page tables to get the physical page, and then map
>> back to kernel space, but this only works as long as the memory is paged in.)
>> Please correct me if I'm wrong.
> 
> I am not good at it, but I think at the first place, you might use
> get_user_pages() (take a look here
> :http://lxr.linux.no/#linux+v2.6.39/mm/memory.c#L1703)
> 
> then once you get the pointer to the  pages (and making sure they're
> pinned by get_user_pages), I think you just need to use kmap().
> 
> I suggest to really observe that scatter gather function and see if
> address in kernel address space is really needed.... if not, you can
> avoid using kmap() completely.
> 

Many thanks for that tip! I've been reading LDD3 ("Linux Device Drivers 3rd
edition") and "Understanding the Linux Kernel" as well as searching google and
of course reading code for many hours, and nowhere has there been a mention of
get_user_pages()! Obviously I haven't been using the right search terms...

(Also, for some reason your mail didn't reach my inbox, so I only saw it a few
days ago when looking at a mail list archive.) 

Anyway, the code looks like this now (below), and seems to work on 2.7.37.6. It
will probably have problems on systems with > 896 MiB and high memory enabled
though; I'm thinking the scatterlist functions won't handle virtual kernel
addresses?

Any ideas for improvements are welcome; especially if I'm doing something
stupid that risks a kernel panic.


static int proc_pid_text_checksum(struct seq_file *m, struct pid_namespace *ns,
				struct pid *pid, struct task_struct *task)
{
	int retval;

	int text_size;
	int nr_pages, nr_pages_mapped;
	int i;
	struct page **pages;

	struct scatterlist *sgl, *sg;

	u8 result[MD5_DIGEST_SIZE + 2];
	struct crypto_hash *tfm;
	struct hash_desc desc;

	retval = 0;

	if (!task->mm)
		return -EINVAL;

	text_size = task->mm->end_code - task->mm->start_code;
	nr_pages = (text_size + PAGE_SIZE - 1) >> PAGE_SHIFT;


	/**** User page code ****/

	pages = kmalloc(nr_pages * sizeof(*pages), GFP_KERNEL);
	if (!pages) {
		retval = -ENOMEM;
		goto err_pages;
	}

	down_read(&task->mm->mmap_sem);
	nr_pages_mapped = get_user_pages(current, task->mm,
			task->mm->start_code, nr_pages, 0, 0, pages, NULL);
	up_read(&task->mm->mmap_sem);

	if (nr_pages_mapped < nr_pages) {
		retval = -EBUSY; /* Weird error code for this,
		                    couldn't find any better */
		goto err_mapped;
	}


	/**** Scatterlist code ****/

	sgl = kmalloc(nr_pages_mapped * sizeof(*sgl), GFP_KERNEL);
	if (!sgl) {
		retval = -ENOMEM;
		goto err_sg;
	}

	sg_init_table(sgl, nr_pages_mapped);

	for_each_sg(sgl, sg, nr_pages_mapped, i)
		sg_set_page(sg, pages[i], (i < nr_pages_mapped) ? PAGE_SIZE :
						text_size & ~PAGE_MASK, 0);


	/**** Crypto code ****/

	tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
	if (IS_ERR(tfm)) {
		retval = -ENOMEM;
		goto err_crypto;
	}

	desc.tfm = tfm;
	desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;

	memset(result, 0, MD5_DIGEST_SIZE + 2);
	retval = crypto_hash_digest(&desc, sgl, text_size, result);
	if (retval)
		goto err_digest;

	for (i = 0; i < MD5_DIGEST_SIZE; i++)
		seq_printf(m, "%02x", result[i]);
	seq_printf(m, "\n");


err_digest:
	crypto_free_hash(tfm);

err_crypto:
	kfree(sgl);
	for (i = 0; i < nr_pages_mapped; i++)
		put_page(pages[i]);

err_sg:
err_mapped:
	kfree(pages);

err_pages:
	return retval;
}


-- 
Arvid Brodin
Enea Services Stockholm AB



More information about the Kernelnewbies mailing list