[PATCH v2] RDMA/uverbs: Remove flexible arrays from struct *_filter

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When a struct containing a flexible array is included in another struct,
and there is a member after the struct-with-flex-array, there is a
possibility of memory overlap. These cases must be audited [1]. See:

struct inner {
	...
	int flex[];
};

struct outer {
	...
	struct inner header;
	int overlap;
	...
};

This is the scenario for all the "struct *_filter" structures that are
included in the following "struct ib_flow_spec_*" structures:

struct ib_flow_spec_eth
struct ib_flow_spec_ib
struct ib_flow_spec_ipv4
struct ib_flow_spec_ipv6
struct ib_flow_spec_tcp_udp
struct ib_flow_spec_tunnel
struct ib_flow_spec_esp
struct ib_flow_spec_gre
struct ib_flow_spec_mpls

The pattern is like the one shown below:

struct *_filter {
	...
	u8 real_sz[];
};

struct ib_flow_spec_* {
	...
	struct *_filter val;
	struct *_filter mask;
};

In this case, the trailing flexible array "real_sz" is never allocated
and is only used to calculate the size of the structures. Here the use
of the "offsetof" helper can be changed by the "sizeof" operator because
the goal is to get the size of these structures. Therefore, the trailing
flexible arrays can also be removed.

However, due to the trailing padding that can be induced in structs it
is possible that the:

offsetof(struct *_filter, real_sz) != sizeof(struct *_filter)

This situation happens with the "struct ib_flow_ipv6_filter" and to
avoid it the "__packed" macro is used in this structure. But now, the
"sizeof(struct ib_flow_ipv6_filter)" has changed. This is not a problem
since this size is not used in the code.

The situation now is that "sizeof(struct ib_flow_spec_ipv6)" has also
changed (this struct contains the struct ib_flow_ipv6_filter). This is
also not a problem since it is only used to set the size of the "union
ib_flow_spec", which can store all the "ib_flow_spec_*" structures.

Link: https://github.com/KSPP/linux/issues/202 [1]
Signed-off-by: Erick Archer <erick.archer@xxxxxxx>
---
Changes in v2:
- Add the "__packed" macro to the "struct ib_flow_ipv6_filter".
- Update the commit message to explain why the "__packed" macro is used.

Previous versions:
v1: https://lore.kernel.org/linux-hardening/20240211115856.9788-1-erick.archer@xxxxxxx/

Hi Kees and Jason,

First of all, thanks for yours explanations and tips. These were used to get
the final patch.

The steps that I have followed to only add the "__packed" macro to the
"struct ib_flow_ipv6_filter" are the followings:

Step 1: Create new "struct *_filter_new" structures based on "struct *_filter"
        structures but with the trailing flexible arrays removed. Create new
        "struct ib_flow_spec_*_new" structures based on "struct ib_flow_spec_*"
        structures but containing the new "struct *_filter_new" structures.

        Also add a set of assertions to check all the "offsetof" changes made
        in the patch sent. And a set of assertions to verify if the size of
        all the "struct ib_flow_spec_*" structures " has changed.

 drivers/infiniband/core/uverbs_cmd.c |  19 +++++
 include/rdma/ib_verbs.h              | 117 +++++++++++++++++++++++++++
 2 files changed, 136 insertions(+)

diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index 6de05ade2ba9..a7ee65791900 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -2726,6 +2726,25 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type,
 	ssize_t actual_filter_sz;
 	ssize_t ib_filter_sz;

+	static_assert(offsetof(struct ib_flow_eth_filter, real_sz) == sizeof(struct ib_flow_eth_filter_new));
+	static_assert(offsetof(struct ib_flow_ipv4_filter, real_sz) == sizeof(struct ib_flow_ipv4_filter_new));
+	static_assert(offsetof(struct ib_flow_ipv6_filter, real_sz) == sizeof(struct ib_flow_ipv6_filter_new));
+	static_assert(offsetof(struct ib_flow_tcp_udp_filter, real_sz) == sizeof(struct ib_flow_tcp_udp_filter_new));
+	static_assert(offsetof(struct ib_flow_tunnel_filter, real_sz) == sizeof(struct ib_flow_tunnel_filter_new));
+	static_assert(offsetof(struct ib_flow_esp_filter, real_sz) == sizeof(struct ib_flow_esp_filter_new));
+	static_assert(offsetof(struct ib_flow_gre_filter, real_sz) == sizeof(struct ib_flow_gre_filter_new));
+	static_assert(offsetof(struct ib_flow_mpls_filter, real_sz) == sizeof(struct ib_flow_mpls_filter_new));
+
+	static_assert(sizeof(struct ib_flow_spec_eth) == sizeof(struct ib_flow_spec_eth_new));
+	static_assert(sizeof(struct ib_flow_spec_ib) == sizeof(struct ib_flow_spec_ib_new));
+	static_assert(sizeof(struct ib_flow_spec_ipv4) == sizeof(struct ib_flow_spec_ipv4_new));
+	static_assert(sizeof(struct ib_flow_spec_ipv6) == sizeof(struct ib_flow_spec_ipv6_new));
+	static_assert(sizeof(struct ib_flow_spec_tcp_udp) == sizeof(struct ib_flow_spec_tcp_udp_new));
+	static_assert(sizeof(struct ib_flow_spec_tunnel) == sizeof(struct ib_flow_spec_tunnel_new));
+	static_assert(sizeof(struct ib_flow_spec_esp) == sizeof(struct ib_flow_spec_esp_new));
+	static_assert(sizeof(struct ib_flow_spec_gre) == sizeof(struct ib_flow_spec_gre_new));
+	static_assert(sizeof(struct ib_flow_spec_mpls) == sizeof(struct ib_flow_spec_mpls_new));
+
 	/* User flow spec size must be aligned to 4 bytes */
 	if (kern_filter_sz != ALIGN(kern_filter_sz, 4))
 		return -EINVAL;
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index b7b6b58dd348..6ee3110c008e 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -1914,6 +1914,13 @@ struct ib_flow_eth_filter {
 	u8	real_sz[];
 };

+struct ib_flow_eth_filter_new {
+	u8	dst_mac[6];
+	u8	src_mac[6];
+	__be16	ether_type;
+	__be16	vlan_tag;
+};
+
 struct ib_flow_spec_eth {
 	u32			  type;
 	u16			  size;
@@ -1921,6 +1928,13 @@ struct ib_flow_spec_eth {
 	struct ib_flow_eth_filter mask;
 };

+struct ib_flow_spec_eth_new {
+	u32			      type;
+	u16			      size;
+	struct ib_flow_eth_filter_new val;
+	struct ib_flow_eth_filter_new mask;
+};
+
 struct ib_flow_ib_filter {
 	__be16 dlid;
 	__u8   sl;
@@ -1928,6 +1942,11 @@ struct ib_flow_ib_filter {
 	u8	real_sz[];
 };

+struct ib_flow_ib_filter_new {
+	__be16 dlid;
+	__u8   sl;
+};
+
 struct ib_flow_spec_ib {
 	u32			 type;
 	u16			 size;
@@ -1935,6 +1954,13 @@ struct ib_flow_spec_ib {
 	struct ib_flow_ib_filter mask;
 };

+struct ib_flow_spec_ib_new {
+	u32			     type;
+	u16			     size;
+	struct ib_flow_ib_filter_new val;
+	struct ib_flow_ib_filter_new mask;
+};
+
 /* IPv4 header flags */
 enum ib_ipv4_flags {
 	IB_IPV4_DONT_FRAG = 0x2, /* Don't enable packet fragmentation */
@@ -1953,6 +1979,15 @@ struct ib_flow_ipv4_filter {
 	u8	real_sz[];
 };

+struct ib_flow_ipv4_filter_new {
+	__be32	src_ip;
+	__be32	dst_ip;
+	u8	proto;
+	u8	tos;
+	u8	ttl;
+	u8	flags;
+};
+
 struct ib_flow_spec_ipv4 {
 	u32			   type;
 	u16			   size;
@@ -1960,6 +1995,13 @@ struct ib_flow_spec_ipv4 {
 	struct ib_flow_ipv4_filter mask;
 };

+struct ib_flow_spec_ipv4_new {
+	u32			       type;
+	u16			       size;
+	struct ib_flow_ipv4_filter_new val;
+	struct ib_flow_ipv4_filter_new mask;
+};
+
 struct ib_flow_ipv6_filter {
 	u8	src_ip[16];
 	u8	dst_ip[16];
@@ -1971,6 +2013,15 @@ struct ib_flow_ipv6_filter {
 	u8	real_sz[];
 };

+struct ib_flow_ipv6_filter_new {
+	u8	src_ip[16];
+	u8	dst_ip[16];
+	__be32	flow_label;
+	u8	next_hdr;
+	u8	traffic_class;
+	u8	hop_limit;
+};
+
 struct ib_flow_spec_ipv6 {
 	u32			   type;
 	u16			   size;
@@ -1978,6 +2029,13 @@ struct ib_flow_spec_ipv6 {
 	struct ib_flow_ipv6_filter mask;
 };

+struct ib_flow_spec_ipv6_new {
+	u32			       type;
+	u16			       size;
+	struct ib_flow_ipv6_filter_new val;
+	struct ib_flow_ipv6_filter_new mask;
+};
+
 struct ib_flow_tcp_udp_filter {
 	__be16	dst_port;
 	__be16	src_port;
@@ -1985,6 +2043,11 @@ struct ib_flow_tcp_udp_filter {
 	u8	real_sz[];
 };

+struct ib_flow_tcp_udp_filter_new {
+	__be16	dst_port;
+	__be16	src_port;
+};
+
 struct ib_flow_spec_tcp_udp {
 	u32			      type;
 	u16			      size;
@@ -1992,11 +2055,22 @@ struct ib_flow_spec_tcp_udp {
 	struct ib_flow_tcp_udp_filter mask;
 };

+struct ib_flow_spec_tcp_udp_new {
+	u32				  type;
+	u16				  size;
+	struct ib_flow_tcp_udp_filter_new val;
+	struct ib_flow_tcp_udp_filter_new mask;
+};
+
 struct ib_flow_tunnel_filter {
 	__be32	tunnel_id;
 	u8	real_sz[];
 };

+struct ib_flow_tunnel_filter_new {
+	__be32	tunnel_id;
+};
+
 /* ib_flow_spec_tunnel describes the Vxlan tunnel
  * the tunnel_id from val has the vni value
  */
@@ -2007,6 +2081,13 @@ struct ib_flow_spec_tunnel {
 	struct ib_flow_tunnel_filter  mask;
 };

+struct ib_flow_spec_tunnel_new {
+	u32				 type;
+	u16				 size;
+	struct ib_flow_tunnel_filter_new val;
+	struct ib_flow_tunnel_filter_new mask;
+};
+
 struct ib_flow_esp_filter {
 	__be32	spi;
 	__be32  seq;
@@ -2014,6 +2095,11 @@ struct ib_flow_esp_filter {
 	u8	real_sz[];
 };

+struct ib_flow_esp_filter_new {
+	__be32	spi;
+	__be32  seq;
+};
+
 struct ib_flow_spec_esp {
 	u32                           type;
 	u16			      size;
@@ -2021,6 +2107,13 @@ struct ib_flow_spec_esp {
 	struct ib_flow_esp_filter     mask;
 };

+struct ib_flow_spec_esp_new {
+	u32			      type;
+	u16			      size;
+	struct ib_flow_esp_filter_new val;
+	struct ib_flow_esp_filter_new mask;
+};
+
 struct ib_flow_gre_filter {
 	__be16 c_ks_res0_ver;
 	__be16 protocol;
@@ -2029,6 +2122,12 @@ struct ib_flow_gre_filter {
 	u8	real_sz[];
 };

+struct ib_flow_gre_filter_new {
+	__be16 c_ks_res0_ver;
+	__be16 protocol;
+	__be32 key;
+};
+
 struct ib_flow_spec_gre {
 	u32                           type;
 	u16			      size;
@@ -2036,12 +2135,23 @@ struct ib_flow_spec_gre {
 	struct ib_flow_gre_filter     mask;
 };

+struct ib_flow_spec_gre_new {
+	u32			      type;
+	u16			      size;
+	struct ib_flow_gre_filter_new val;
+	struct ib_flow_gre_filter_new mask;
+};
+
 struct ib_flow_mpls_filter {
 	__be32 tag;
 	/* Must be last */
 	u8	real_sz[];
 };

+struct ib_flow_mpls_filter_new {
+	__be32 tag;
+};
+
 struct ib_flow_spec_mpls {
 	u32                           type;
 	u16			      size;
@@ -2049,6 +2159,13 @@ struct ib_flow_spec_mpls {
 	struct ib_flow_mpls_filter     mask;
 };

+struct ib_flow_spec_mpls_new {
+	u32			       type;
+	u16			       size;
+	struct ib_flow_mpls_filter_new val;
+	struct ib_flow_mpls_filter_new mask;
+};
+
 struct ib_flow_spec_action_tag {
 	enum ib_flow_spec_type	      type;
 	u16			      size;

The built of this patch shows an assertion error:

static assertion failed:
drivers/infiniband/core/uverbs_cmd.c:2731:2: note: in expansion of macro ‘static_assert’
 2731 |  static_assert(offsetof(struct ib_flow_ipv6_filter, real_sz) == sizeof(struct ib_flow_ipv6_filter_new));

Step 2: Add the "__packed" macro to "struct ib_flow_ipv6_filter_new".

 include/rdma/ib_verbs.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index 6ee3110c008e..e10a2bf4e0fe 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -2020,7 +2020,7 @@ struct ib_flow_ipv6_filter_new {
 	u8	next_hdr;
 	u8	traffic_class;
 	u8	hop_limit;
-};
+} __packed;

 struct ib_flow_spec_ipv6 {
 	u32			   type;

The built of this patch passes the last assertion error but shows a
new error:

static assertion failed:
drivers/infiniband/core/uverbs_cmd.c:2741:2: note: in expansion of macro ‘static_assert’
 2741 |  static_assert(sizeof(struct ib_flow_spec_ipv6) == sizeof(struct ib_flow_spec_ipv6_new));

Step 3: Change the comparison operator in the last assertion.

 drivers/infiniband/core/uverbs_cmd.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index a7ee65791900..83f9edd55280 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -2738,7 +2738,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type,
 	static_assert(sizeof(struct ib_flow_spec_eth) == sizeof(struct ib_flow_spec_eth_new));
 	static_assert(sizeof(struct ib_flow_spec_ib) == sizeof(struct ib_flow_spec_ib_new));
 	static_assert(sizeof(struct ib_flow_spec_ipv4) == sizeof(struct ib_flow_spec_ipv4_new));
-	static_assert(sizeof(struct ib_flow_spec_ipv6) == sizeof(struct ib_flow_spec_ipv6_new));
+	static_assert(sizeof(struct ib_flow_spec_ipv6) != sizeof(struct ib_flow_spec_ipv6_new));
 	static_assert(sizeof(struct ib_flow_spec_tcp_udp) == sizeof(struct ib_flow_spec_tcp_udp_new));
 	static_assert(sizeof(struct ib_flow_spec_tunnel) == sizeof(struct ib_flow_spec_tunnel_new));
 	static_assert(sizeof(struct ib_flow_spec_esp) == sizeof(struct ib_flow_spec_esp_new));

Finally the built shows no errors. So, removing all the trailing flexible
arrays and adding the "__packed" macro only affects the size of the
"struct ib_flow_spec_ipv6" structure.

As a final point I would like to comment that the binary comparison
produced results that were complicated for me to analyze due to the
difference in the size of the structures.

Regards,
Erick
---
 drivers/infiniband/core/uverbs_cmd.c | 16 ++++++++--------
 include/rdma/ib_verbs.h              | 19 +------------------
 2 files changed, 9 insertions(+), 26 deletions(-)

diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index 6de05ade2ba9..3d3ee3eca983 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -2737,7 +2737,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type,

 	switch (ib_spec->type & ~IB_FLOW_SPEC_INNER) {
 	case IB_FLOW_SPEC_ETH:
-		ib_filter_sz = offsetof(struct ib_flow_eth_filter, real_sz);
+		ib_filter_sz = sizeof(struct ib_flow_eth_filter);
 		actual_filter_sz = spec_filter_size(kern_spec_mask,
 						    kern_filter_sz,
 						    ib_filter_sz);
@@ -2748,7 +2748,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type,
 		memcpy(&ib_spec->eth.mask, kern_spec_mask, actual_filter_sz);
 		break;
 	case IB_FLOW_SPEC_IPV4:
-		ib_filter_sz = offsetof(struct ib_flow_ipv4_filter, real_sz);
+		ib_filter_sz = sizeof(struct ib_flow_ipv4_filter);
 		actual_filter_sz = spec_filter_size(kern_spec_mask,
 						    kern_filter_sz,
 						    ib_filter_sz);
@@ -2759,7 +2759,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type,
 		memcpy(&ib_spec->ipv4.mask, kern_spec_mask, actual_filter_sz);
 		break;
 	case IB_FLOW_SPEC_IPV6:
-		ib_filter_sz = offsetof(struct ib_flow_ipv6_filter, real_sz);
+		ib_filter_sz = sizeof(struct ib_flow_ipv6_filter);
 		actual_filter_sz = spec_filter_size(kern_spec_mask,
 						    kern_filter_sz,
 						    ib_filter_sz);
@@ -2775,7 +2775,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type,
 		break;
 	case IB_FLOW_SPEC_TCP:
 	case IB_FLOW_SPEC_UDP:
-		ib_filter_sz = offsetof(struct ib_flow_tcp_udp_filter, real_sz);
+		ib_filter_sz = sizeof(struct ib_flow_tcp_udp_filter);
 		actual_filter_sz = spec_filter_size(kern_spec_mask,
 						    kern_filter_sz,
 						    ib_filter_sz);
@@ -2786,7 +2786,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type,
 		memcpy(&ib_spec->tcp_udp.mask, kern_spec_mask, actual_filter_sz);
 		break;
 	case IB_FLOW_SPEC_VXLAN_TUNNEL:
-		ib_filter_sz = offsetof(struct ib_flow_tunnel_filter, real_sz);
+		ib_filter_sz = sizeof(struct ib_flow_tunnel_filter);
 		actual_filter_sz = spec_filter_size(kern_spec_mask,
 						    kern_filter_sz,
 						    ib_filter_sz);
@@ -2801,7 +2801,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type,
 			return -EINVAL;
 		break;
 	case IB_FLOW_SPEC_ESP:
-		ib_filter_sz = offsetof(struct ib_flow_esp_filter, real_sz);
+		ib_filter_sz = sizeof(struct ib_flow_esp_filter);
 		actual_filter_sz = spec_filter_size(kern_spec_mask,
 						    kern_filter_sz,
 						    ib_filter_sz);
@@ -2812,7 +2812,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type,
 		memcpy(&ib_spec->esp.mask, kern_spec_mask, actual_filter_sz);
 		break;
 	case IB_FLOW_SPEC_GRE:
-		ib_filter_sz = offsetof(struct ib_flow_gre_filter, real_sz);
+		ib_filter_sz = sizeof(struct ib_flow_gre_filter);
 		actual_filter_sz = spec_filter_size(kern_spec_mask,
 						    kern_filter_sz,
 						    ib_filter_sz);
@@ -2823,7 +2823,7 @@ int ib_uverbs_kern_spec_to_ib_spec_filter(enum ib_flow_spec_type type,
 		memcpy(&ib_spec->gre.mask, kern_spec_mask, actual_filter_sz);
 		break;
 	case IB_FLOW_SPEC_MPLS:
-		ib_filter_sz = offsetof(struct ib_flow_mpls_filter, real_sz);
+		ib_filter_sz = sizeof(struct ib_flow_mpls_filter);
 		actual_filter_sz = spec_filter_size(kern_spec_mask,
 						    kern_filter_sz,
 						    ib_filter_sz);
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index b7b6b58dd348..477bf9dd5e71 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -1910,8 +1910,6 @@ struct ib_flow_eth_filter {
 	u8	src_mac[6];
 	__be16	ether_type;
 	__be16	vlan_tag;
-	/* Must be last */
-	u8	real_sz[];
 };

 struct ib_flow_spec_eth {
@@ -1924,8 +1922,6 @@ struct ib_flow_spec_eth {
 struct ib_flow_ib_filter {
 	__be16 dlid;
 	__u8   sl;
-	/* Must be last */
-	u8	real_sz[];
 };

 struct ib_flow_spec_ib {
@@ -1949,8 +1945,6 @@ struct ib_flow_ipv4_filter {
 	u8	tos;
 	u8	ttl;
 	u8	flags;
-	/* Must be last */
-	u8	real_sz[];
 };

 struct ib_flow_spec_ipv4 {
@@ -1967,9 +1961,7 @@ struct ib_flow_ipv6_filter {
 	u8	next_hdr;
 	u8	traffic_class;
 	u8	hop_limit;
-	/* Must be last */
-	u8	real_sz[];
-};
+} __packed;

 struct ib_flow_spec_ipv6 {
 	u32			   type;
@@ -1981,8 +1973,6 @@ struct ib_flow_spec_ipv6 {
 struct ib_flow_tcp_udp_filter {
 	__be16	dst_port;
 	__be16	src_port;
-	/* Must be last */
-	u8	real_sz[];
 };

 struct ib_flow_spec_tcp_udp {
@@ -1994,7 +1984,6 @@ struct ib_flow_spec_tcp_udp {

 struct ib_flow_tunnel_filter {
 	__be32	tunnel_id;
-	u8	real_sz[];
 };

 /* ib_flow_spec_tunnel describes the Vxlan tunnel
@@ -2010,8 +1999,6 @@ struct ib_flow_spec_tunnel {
 struct ib_flow_esp_filter {
 	__be32	spi;
 	__be32  seq;
-	/* Must be last */
-	u8	real_sz[];
 };

 struct ib_flow_spec_esp {
@@ -2025,8 +2012,6 @@ struct ib_flow_gre_filter {
 	__be16 c_ks_res0_ver;
 	__be16 protocol;
 	__be32 key;
-	/* Must be last */
-	u8	real_sz[];
 };

 struct ib_flow_spec_gre {
@@ -2038,8 +2023,6 @@ struct ib_flow_spec_gre {

 struct ib_flow_mpls_filter {
 	__be32 tag;
-	/* Must be last */
-	u8	real_sz[];
 };

 struct ib_flow_spec_mpls {
--
2.25.1






[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Photo]     [Yosemite News]     [Yosemite Photos]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux